Skip to content

Commit d0cbdb5

Browse files
Implement optimized Firestore rules
Refactor Firestore rules to improve performance and security.
1 parent 4d36450 commit d0cbdb5

File tree

1 file changed

+89
-61
lines changed

1 file changed

+89
-61
lines changed

firestore.rules

Lines changed: 89 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,113 +5,141 @@ service cloud.firestore {
55
match /databases/{database}/documents {
66

77
// ======================================
8-
// RÈGLES FIRESTORE CORRIGÉES ET OPTIMISÉES
8+
// RÈGLES FIRESTORE SÉCURISÉES ET RAPIDES
99
// ======================================
1010

1111
// ======================================
12-
// CAMPAGNES - Accès utilisateur seulement
12+
// CAMPAGNES - Propriétaire seulement
1313
// ======================================
1414
match /campaigns/{campaignId} {
15-
// Lecture : utilisateur authentifié ET propriétaire
16-
allow read: if request.auth != null &&
17-
request.auth.uid == resource.data.userId;
15+
// Lecture/écriture : utilisateur authentifié ET propriétaire
16+
allow read, write: if request.auth != null &&
17+
request.auth.uid == resource.data.userId;
1818

1919
// Création : utilisateur authentifié ET défini comme propriétaire
2020
allow create: if request.auth != null &&
21-
request.auth.uid == request.resource.data.userId;
22-
23-
// Mise à jour : utilisateur authentifié ET propriétaire existant
24-
allow update: if request.auth != null &&
25-
request.auth.uid == resource.data.userId;
26-
27-
// Suppression : utilisateur authentifié ET propriétaire
28-
allow delete: if request.auth != null &&
29-
request.auth.uid == resource.data.userId;
21+
request.auth.uid == request.resource.data.userId &&
22+
request.resource.data.keys().hasAll(['name', 'description', 'userId']) &&
23+
request.resource.data.userId is string;
3024
}
3125

3226
// ======================================
33-
// AFFILIÉS - Accès utilisateur seulement
27+
// AFFILIÉS - Propriétaire seulement
3428
// ======================================
3529
match /affiliates/{affiliateId} {
36-
// Lecture : utilisateur authentifié ET propriétaire
37-
allow read: if request.auth != null &&
38-
request.auth.uid == resource.data.userId;
30+
// Lecture/écriture : utilisateur authentifié ET propriétaire
31+
allow read, write: if request.auth != null &&
32+
request.auth.uid == resource.data.userId;
3933

4034
// Création : utilisateur authentifié ET défini comme propriétaire
4135
allow create: if request.auth != null &&
42-
request.auth.uid == request.resource.data.userId;
43-
44-
// Mise à jour : utilisateur authentifié ET propriétaire existant
45-
allow update: if request.auth != null &&
46-
request.auth.uid == resource.data.userId;
47-
48-
// Suppression : utilisateur authentifié ET propriétaire
49-
allow delete: if request.auth != null &&
50-
request.auth.uid == resource.data.userId;
36+
request.auth.uid == request.resource.data.userId &&
37+
request.resource.data.keys().hasAll(['name', 'email', 'campaignId', 'userId']) &&
38+
request.resource.data.userId is string &&
39+
request.resource.data.campaignId is string;
5140
}
5241

5342
// ======================================
54-
// CLICS - Plus permissif pour le tracking
43+
// CLICS - Protection anti-falsification
5544
// ======================================
5645
match /clicks/{clickId} {
57-
// Lecture : tous les utilisateurs authentifiés
58-
allow read: if request.auth != null;
46+
// Lecture : utilisateur authentifié ET (propriétaire OU propriétaire de la campagne)
47+
allow read: if request.auth != null && (
48+
request.auth.uid == resource.data.userId ||
49+
exists(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)) &&
50+
get(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)).data.userId == request.auth.uid
51+
);
5952

60-
// Création : tous les utilisateurs authentifiés (pour tracking public)
61-
allow create: if request.auth != null;
53+
// Création : validation stricte des données
54+
allow create: if request.auth != null &&
55+
request.resource.data.keys().hasAll(['affiliateId', 'campaignId', 'timestamp', 'targetUrl']) &&
56+
request.resource.data.affiliateId is string &&
57+
request.resource.data.campaignId is string &&
58+
request.resource.data.timestamp is timestamp &&
59+
request.resource.data.targetUrl is string &&
60+
// Vérifier que l'affilié existe et appartient à la bonne campagne
61+
exists(/databases/$(database)/documents/affiliates/$(request.resource.data.affiliateId)) &&
62+
get(/databases/$(database)/documents/affiliates/$(request.resource.data.affiliateId)).data.campaignId == request.resource.data.campaignId;
6263

63-
// Mise à jour : propriétaire seulement
64-
allow update: if request.auth != null &&
65-
request.auth.uid == resource.data.userId;
64+
// Mise à jour : interdite pour éviter la falsification
65+
allow update: if false;
6666

67-
// Suppression : propriétaire seulement
68-
allow delete: if request.auth != null &&
69-
request.auth.uid == resource.data.userId;
67+
// Suppression : propriétaire de la campagne seulement
68+
allow delete: if request.auth != null &&
69+
exists(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)) &&
70+
get(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)).data.userId == request.auth.uid;
7071
}
7172

7273
// ======================================
73-
// CONVERSIONS - Plus permissif pour le tracking
74+
// CONVERSIONS - Protection anti-falsification renforcée
7475
// ======================================
7576
match /conversions/{conversionId} {
76-
// Lecture : tous les utilisateurs authentifiés
77-
allow read: if request.auth != null;
77+
// Lecture : utilisateur authentifié ET propriétaire de la campagne
78+
allow read: if request.auth != null &&
79+
exists(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)) &&
80+
get(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)).data.userId == request.auth.uid;
7881

79-
// Création : tous les utilisateurs authentifiés (pour tracking public)
80-
allow create: if request.auth != null;
82+
// Création : validation ultra-stricte
83+
allow create: if request.auth != null &&
84+
request.resource.data.keys().hasAll(['affiliateId', 'campaignId', 'amount', 'commission', 'timestamp']) &&
85+
request.resource.data.affiliateId is string &&
86+
request.resource.data.campaignId is string &&
87+
request.resource.data.amount is number &&
88+
request.resource.data.commission is number &&
89+
request.resource.data.timestamp is timestamp &&
90+
request.resource.data.amount >= 0 &&
91+
request.resource.data.commission >= 0 &&
92+
// Vérifier que l'affilié existe et appartient à la bonne campagne
93+
exists(/databases/$(database)/documents/affiliates/$(request.resource.data.affiliateId)) &&
94+
get(/databases/$(database)/documents/affiliates/$(request.resource.data.affiliateId)).data.campaignId == request.resource.data.campaignId &&
95+
// Vérifier que l'utilisateur possède la campagne
96+
exists(/databases/$(database)/documents/campaigns/$(request.resource.data.campaignId)) &&
97+
get(/databases/$(database)/documents/campaigns/$(request.resource.data.campaignId)).data.userId == request.auth.uid;
8198

82-
// Mise à jour : propriétaire seulement
83-
allow update: if request.auth != null &&
84-
request.auth.uid == resource.data.userId;
99+
// Mise à jour : seulement pour le statut de vérification par le propriétaire
100+
allow update: if request.auth != null &&
101+
request.resource.data.diff(resource.data).affectedKeys().hasOnly(['verified']) &&
102+
exists(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)) &&
103+
get(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)).data.userId == request.auth.uid;
85104

86-
// Suppression : propriétaire seulement
87-
allow delete: if request.auth != null &&
88-
request.auth.uid == resource.data.userId;
105+
// Suppression : propriétaire de la campagne seulement
106+
allow delete: if request.auth != null &&
107+
exists(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)) &&
108+
get(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)).data.userId == request.auth.uid;
89109
}
90110

91111
// ======================================
92112
// LIENS COURTS - Lecture publique nécessaire
93113
// ======================================
94114
match /shortLinks/{linkId} {
95-
// Lecture publique (pour redirection)
115+
// Lecture publique (nécessaire pour redirection)
96116
allow read: if true;
97117

98-
// Création : utilisateur authentifié
99-
allow create: if request.auth != null;
118+
// Création : utilisateur authentifié avec validation
119+
allow create: if request.auth != null &&
120+
request.resource.data.keys().hasAll(['shortCode', 'campaignId', 'affiliateId', 'targetUrl']) &&
121+
request.resource.data.shortCode is string &&
122+
request.resource.data.campaignId is string &&
123+
request.resource.data.affiliateId is string &&
124+
request.resource.data.targetUrl is string &&
125+
// Vérifier que l'utilisateur possède la campagne
126+
exists(/databases/$(database)/documents/campaigns/$(request.resource.data.campaignId)) &&
127+
get(/databases/$(database)/documents/campaigns/$(request.resource.data.campaignId)).data.userId == request.auth.uid;
100128

101-
// Mise à jour : propriétaire seulement
102-
allow update: if request.auth != null &&
103-
request.auth.uid == resource.data.userId;
129+
// Mise à jour : propriétaire de la campagne seulement
130+
allow update: if request.auth != null &&
131+
exists(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)) &&
132+
get(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)).data.userId == request.auth.uid;
104133

105-
// Suppression : propriétaire seulement
106-
allow delete: if request.auth != null &&
107-
request.auth.uid == resource.data.userId;
134+
// Suppression : propriétaire de la campagne seulement
135+
allow delete: if request.auth != null &&
136+
exists(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)) &&
137+
get(/databases/$(database)/documents/campaigns/$(resource.data.campaignId)).data.userId == request.auth.uid;
108138
}
109139

110140
// ======================================
111-
// SÉCURITÉ PAR DÉFAUT
141+
// SÉCURITÉ PAR DÉFAUT - Bloquer tout le reste
112142
// ======================================
113-
114-
// Bloquer tout le reste
115143
match /{document=**} {
116144
allow read, write: if false;
117145
}

0 commit comments

Comments
 (0)