- Einladen per einmaligem 6-stelligem Code (7 Tage gültig), wahlweise als Mitglied oder Pflanzen-Sitter - Beitritt über Cloud Function joinHousehold (Transaktion, Admin-Rechte) - Security Rules: Sitter dürfen an Pflanzen nur Bestätigungs-Felder ändern; invites nur erstellen, nie lesen - Haushalts-Screen: Mitgliederliste mit Rollen, Einladen, Beitreten mit Wechsel-Warnung - UI-Gating: Sitter sehen keine Anlegen-/Bearbeiten-/Löschen-Aktionen - Bestätigungen speichern den Namen (lastWateredBy/lastFertilizedBy), Anzeige im Profil - Tests: Members-Map im Seed, neuer Sitter-Test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
83 lines
3.1 KiB
Text
83 lines
3.1 KiB
Text
rules_version = '2';
|
||
|
||
// Sicherheitsregeln für LeafItToMe.
|
||
//
|
||
// Grundprinzip: Alle App-Daten hängen an einem Haushalt. Lesen darf nur,
|
||
// wer im Feld `memberUids` des Haushalts steht. Schreiben hängt zusätzlich
|
||
// von der Rolle ab: "member" darf alles, "sitter" (Pflanzen-Sitter) darf
|
||
// nur Aufgaben bestätigen. Das eigene Nutzer-Dokument sieht nur der Nutzer.
|
||
service cloud.firestore {
|
||
match /databases/{database}/documents {
|
||
|
||
function signedIn() {
|
||
return request.auth != null;
|
||
}
|
||
|
||
function householdData(householdId) {
|
||
return get(/databases/$(database)/documents/households/$(householdId)).data;
|
||
}
|
||
|
||
function isMember(householdId) {
|
||
return signedIn()
|
||
&& request.auth.uid in householdData(householdId).memberUids;
|
||
}
|
||
|
||
// Rolle des aufrufenden Nutzers. Haushalte aus der Zeit vor V2 haben
|
||
// noch keine members-Map – dann gilt jeder Eingetragene als "member".
|
||
function roleOf(householdId) {
|
||
return householdData(householdId)
|
||
.get('members', {})
|
||
.get(request.auth.uid, {'role': 'member'})
|
||
.get('role', 'member');
|
||
}
|
||
|
||
// Eigenes Nutzerprofil (enthält u. a. die householdId).
|
||
match /users/{uid} {
|
||
allow read, write: if signedIn() && request.auth.uid == uid;
|
||
}
|
||
|
||
// Einladungscodes: Erstellen darf jedes Mitglied des jeweiligen
|
||
// Haushalts. Gelesen und eingelöst werden Codes ausschließlich über
|
||
// die Cloud Function joinHousehold (Admin-Rechte) – Clients nie direkt.
|
||
match /invites/{code} {
|
||
allow create: if signedIn()
|
||
&& request.resource.data.createdBy == request.auth.uid
|
||
&& isMember(request.resource.data.householdId);
|
||
allow read, update, delete: if false;
|
||
}
|
||
|
||
match /households/{householdId} {
|
||
allow read: if signedIn() && request.auth.uid in resource.data.memberUids;
|
||
// Ändern (z. B. Name) nur für volle Mitglieder; Mitglieder-Verwaltung
|
||
// läuft über die Cloud Function.
|
||
allow update: if signedIn()
|
||
&& request.auth.uid in resource.data.memberUids
|
||
&& resource.data.get('members', {})
|
||
.get(request.auth.uid, {'role': 'member'})
|
||
.get('role', 'member') == 'member';
|
||
// Anlegen nur, wenn man sich selbst als Mitglied einträgt
|
||
// (passiert automatisch bei der Registrierung).
|
||
allow create: if signedIn()
|
||
&& request.auth.uid in request.resource.data.memberUids;
|
||
allow delete: if false;
|
||
|
||
match /plants/{plantId} {
|
||
allow read: if isMember(householdId);
|
||
allow create, delete: if isMember(householdId)
|
||
&& roleOf(householdId) == 'member';
|
||
// Sitter dürfen ausschließlich die Bestätigungs-Felder ändern.
|
||
allow update: if isMember(householdId)
|
||
&& (roleOf(householdId) == 'member'
|
||
|| request.resource.data.diff(resource.data).affectedKeys()
|
||
.hasOnly(['lastWatered', 'lastWateredBy',
|
||
'lastFertilized', 'lastFertilizedBy']));
|
||
}
|
||
|
||
match /locations/{locationId} {
|
||
allow read: if isMember(householdId);
|
||
allow write: if isMember(householdId)
|
||
&& roleOf(householdId) == 'member';
|
||
}
|
||
}
|
||
}
|
||
}
|