Repository navigation
Expand file tree
/
Copy pathfirestore.rules
More file actions
65 lines (56 loc) · 2.59 KB
/
Copy pathfirestore.rules
File metadata and controls
65 lines (56 loc) · 2.59 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
rules_version = '2';
// SecureVault data model (all secrets are encrypted client-side before they reach Firestore):
// users/{uid}/config/crypto { salt: string, verifier?: { data: string, iv: string } }
// users/{uid}/passwords/{id} { encryptedData, iv, tags, createdAt, updatedAt }
service cloud.firestore {
match /databases/{database}/documents {
function isOwner(userId) {
return request.auth != null && request.auth.uid == userId;
}
function validConfig(d) {
return d.keys().hasOnly(['salt', 'verifier'])
&& d.salt is string && d.salt.size() > 0 && d.salt.size() <= 64
&& (!('verifier' in d) || (
d.verifier is map
&& d.verifier.keys().hasOnly(['data', 'iv'])
&& d.verifier.data is string && d.verifier.data.size() <= 256
&& d.verifier.iv is string && d.verifier.iv.size() <= 32));
}
function validEntry(d) {
return d.keys().hasOnly(['encryptedData', 'iv', 'tags', 'createdAt', 'updatedAt'])
&& d.encryptedData is string && d.encryptedData.size() > 0 && d.encryptedData.size() <= 100000
&& d.iv is string && d.iv.size() <= 32
&& d.tags is list && d.tags.size() <= 50
&& d.createdAt is timestamp
&& d.updatedAt is timestamp;
}
match /users/{userId} {
// Nothing is stored on the user document itself. Reads/deletes are allowed so older
// data written by early versions of the extension can still be cleaned up.
allow read, delete: if isOwner(userId);
match /config/crypto {
allow read, delete: if isOwner(userId);
allow create: if isOwner(userId) && validConfig(request.resource.data);
// The salt is permanent: changing it would make every entry undecryptable.
// Updates exist only to add a verifier to accounts created before verifiers existed.
allow update: if isOwner(userId)
&& validConfig(request.resource.data)
&& request.resource.data.salt == resource.data.salt;
}
match /passwords/{entryId} {
allow read, delete: if isOwner(userId);
allow create: if isOwner(userId)
&& validEntry(request.resource.data)
&& request.resource.data.createdAt == request.time
&& request.resource.data.updatedAt == request.time;
allow update: if isOwner(userId)
&& validEntry(request.resource.data)
&& request.resource.data.createdAt == resource.data.createdAt
&& request.resource.data.updatedAt == request.time;
}
}
match /{document=**} {
allow read, write: if false;
}
}
}