feat(date-memories): add date_history and date_reflections Firestore security rules
This commit is contained in:
parent
4dd60a6a4d
commit
602ab3a260
|
|
@ -643,6 +643,47 @@ service cloud.firestore {
|
|||
allow delete: if isCouplesMember(coupleId);
|
||||
}
|
||||
|
||||
// Date Replay — completed-date log. PLAINTEXT app metadata (date-idea title/category + timestamp,
|
||||
// not private words; the reflection content is E2EE below). Idempotent merge on doc id = matchId.
|
||||
match /date_history/{dateId} {
|
||||
allow read: if isCouplesMember(coupleId);
|
||||
allow create, update: if isCouplesMember(coupleId)
|
||||
&& request.resource.data.keys().hasOnly(['dateIdeaId', 'title', 'category', 'completedAt', 'addedBy'])
|
||||
&& request.resource.data.addedBy is string
|
||||
&& request.resource.data.completedAt is number;
|
||||
allow delete: if isCouplesMember(coupleId);
|
||||
}
|
||||
|
||||
// Date reflection metadata: each user writes their own; both members read (drives "your turn").
|
||||
// Mirrors daily_question/answers — encrypted content is in the read-gated `secure` subdoc below.
|
||||
match /date_reflections/{dateId}/answers/{userId} {
|
||||
allow read: if isCouplesMember(coupleId);
|
||||
allow create: if isCouplesMember(coupleId)
|
||||
&& request.auth.uid == userId
|
||||
&& request.resource.data.userId == request.auth.uid
|
||||
&& request.resource.data.isRevealed == false
|
||||
&& request.resource.data.keys().hasOnly(['userId', 'schemaVersion', 'createdAt', 'updatedAt', 'isRevealed']);
|
||||
allow update: if isCouplesMember(coupleId)
|
||||
&& request.auth.uid == userId
|
||||
&& request.resource.data.userId == resource.data.userId
|
||||
// Only the reveal flag may flip; the encrypted payload is immutable.
|
||||
&& request.resource.data.diff(resource.data).affectedKeys().hasOnly(['isRevealed', 'updatedAt']);
|
||||
allow delete: if false;
|
||||
|
||||
// Couple-key encrypted reflection content. Read-gated: you can read your PARTNER's content only
|
||||
// once YOU have also reflected (the "private until both" gate). Your own content is always readable.
|
||||
match /secure/{doc} {
|
||||
allow read: if isCouplesMember(coupleId)
|
||||
&& (request.auth.uid == userId
|
||||
|| exists(/databases/$(database)/documents/couples/$(coupleId)/date_reflections/$(dateId)/answers/$(request.auth.uid)));
|
||||
allow create: if isCouplesMember(coupleId)
|
||||
&& request.auth.uid == userId
|
||||
&& isCiphertext(request.resource.data.encryptedPayload)
|
||||
&& request.resource.data.keys().hasOnly(['encryptedPayload']);
|
||||
allow update, delete: if false;
|
||||
}
|
||||
}
|
||||
|
||||
// Couple Lore stores revealed answer summaries. Summary text must remain
|
||||
// encrypted with the couple key; prompts/metadata can stay plaintext.
|
||||
match /lore/{loreId} {
|
||||
|
|
|
|||
Loading…
Reference in New Issue