feat(rules): add read-gated secure subdoc for couple-key encrypted answers (schemaVersion 2)
This commit is contained in:
parent
f7418df700
commit
e5c9c43317
|
|
@ -117,6 +117,25 @@ service cloud.firestore {
|
|||
.hasOnly(['isRevealed', 'answerKeyReleased', 'updatedAt']);
|
||||
}
|
||||
|
||||
// Couple-key daily answer (schemaVersion 2): the answer doc is metadata only (no content).
|
||||
// The encrypted content lives in the read-gated `secure` subdoc, so this doc can stay
|
||||
// readable (drives the partner's "your turn" indicator) without leaking the answer.
|
||||
function isCoupleKeyAnswerCreate(data) {
|
||||
return data.keys().hasOnly([
|
||||
'userId', 'questionId', 'answerType',
|
||||
'schemaVersion', 'answerDate', 'createdAt', 'updatedAt', 'isRevealed'
|
||||
])
|
||||
&& data.schemaVersion == 2
|
||||
&& data.isRevealed == false;
|
||||
}
|
||||
|
||||
// After a couple-key answer is created, only the reveal flag may flip (drives the opened push).
|
||||
function isCoupleKeyAnswerUpdate() {
|
||||
return resource.data.schemaVersion == 2
|
||||
&& request.resource.data.diff(resource.data).affectedKeys()
|
||||
.hasOnly(['isRevealed', 'updatedAt']);
|
||||
}
|
||||
|
||||
// Thread sealed answers differ from daily answers: no answerDate (threads use threadId
|
||||
// as context), no isRevealed field (reveal state is tracked by the thread VM).
|
||||
function isSealedThreadAnswerCreate(data) {
|
||||
|
|
@ -670,6 +689,9 @@ service cloud.firestore {
|
|||
|
||||
// Daily question answers: each user writes their own; both members read.
|
||||
match /daily_question/{date}/answers/{userId} {
|
||||
// The answer doc holds only metadata (no content) so the partner can see THAT you
|
||||
// answered ("your turn / waiting for you"); the encrypted content lives in the
|
||||
// read-gated `secure` subdoc below. Both members may read metadata.
|
||||
allow read: if isCouplesMember(coupleId);
|
||||
|
||||
allow create: if isCouplesMember(coupleId)
|
||||
|
|
@ -681,16 +703,17 @@ service cloud.firestore {
|
|||
// whose metadata disagrees with the path it lands in.
|
||||
&& request.resource.data.answerDate is string
|
||||
&& request.resource.data.answerDate == date
|
||||
// schemaVersion 3: partner-proof sealed answer (the only accepted shape).
|
||||
&& isSealedAnswerCreate(request.resource.data);
|
||||
// schemaVersion 2 = couple-key (current); 3 = legacy sealed partner-proof.
|
||||
&& (isCoupleKeyAnswerCreate(request.resource.data)
|
||||
|| isSealedAnswerCreate(request.resource.data));
|
||||
|
||||
allow update: if isCouplesMember(coupleId)
|
||||
&& request.auth.uid == userId
|
||||
&& request.resource.data.userId == resource.data.userId
|
||||
&& request.resource.data.questionId == resource.data.questionId
|
||||
&& request.resource.data.answerType == resource.data.answerType
|
||||
// Sealed answers: only reveal metadata may change; payload is immutable.
|
||||
&& isSealedAnswerUpdate();
|
||||
// Only reveal metadata may change; the encrypted payload is immutable.
|
||||
&& (isCoupleKeyAnswerUpdate() || isSealedAnswerUpdate());
|
||||
|
||||
allow delete: if false;
|
||||
|
||||
|
|
@ -718,6 +741,20 @@ service cloud.firestore {
|
|||
allow update: if false;
|
||||
allow delete: if false;
|
||||
}
|
||||
|
||||
// Couple-key encrypted answer content (schemaVersion 2). Read-gated: you can read your
|
||||
// PARTNER's content only once YOU have also answered — the cryptographic "private until
|
||||
// both answered" 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)/daily_question/$(date)/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;
|
||||
}
|
||||
}
|
||||
|
||||
// Games use enc:v1: (schemaVersion 2 / shared couple key).
|
||||
|
|
|
|||
Loading…
Reference in New Issue