Pour ma vie, je ne peux pas comprendre pourquoi ce qui suit a pour résultat une false
pour permettre des écritures. Supposons que ma collection users
soit vide pour commencer et que j'écris un document au format suivant à partir de mon interface angulaire:
{
displayName: 'FooBar',
email: '[email protected]'
}
Mes règles de sécurité actuelles:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
function isAdmin() {
return resource.data.role == 'ADMIN';
}
function isEditingRole() {
return request.resource.data.role != null;
}
function isEditingOwnRole() {
return isOwnDocument() && isEditingRole();
}
function isOwnDocument() {
return request.auth.uid == userId;
}
allow read: if isOwnDocument() || isAdmin();
allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
}
}
}
En général, je ne veux pas que les utilisateurs puissent éditer leur propre rôle. Les utilisateurs normaux peuvent éditer leur propre document autrement, et les administrateurs peuvent éditer n'importe qui.
Remplacer isEditingRole()
pour false
donne le résultat attendu. Je l'ai donc réduit à cette expression.
L'écriture revient toujours fausse, et je ne peux pas déterminer pourquoi. Toute idée ou solution serait utile!
Modifier 1
Choses que j'ai essayées:
function isEditingRole() {
return request.resource.data.keys().hasAny(['role']);
}
et
function isEditingRole() {
return 'role' in request.resource.data;
}
et
function isEditingRole() {
return 'role' in request.resource.data.keys();
}
Edit 2
Notez que les administrateurs vont éventuellement définir un rôle pour les utilisateurs. Par conséquent, un rôle pourrait éventuellement exister sur un document. Cela signifie que, selon les docs Firestore ci-dessous, la demande aura une clé role
, même si elle n'était pas dans la demande d'origine.
Les champs non fournis dans la demande qui existent dans la ressource sont ajoutés à
request.resource.data
. Les règles peuvent tester si un champ est modifié en comparantrequest.resource.data.foo
àresource.data.foo
, sachant que chaque champ de laresource
sera également présent dansrequest.resource
même s'il n'a pas été soumis dans la demande d'écriture.
Selon cela, je pense que les trois options de "Edit 1" sont exclues. J'ai essayé la suggestion de request.resource.data.role != resource.data.role
et cela ne fonctionne pas non plus ... Je suis perdue et je commence à me demander s'il existe réellement un bug dans Firestore.
Donc au final, il me semble que je supposais que resource.data.nonExistentField == null
renverrait false
, alors qu’il renverrait en fait une Error
(selon this et mes tests). Donc, ma solution initiale a peut-être été confrontée à cela. C'est étonnant, car l'inverse devrait fonctionner selon les docs , mais peut-être les docs font-ils référence à une valeur "non-existante", plutôt qu'à la clé - une distinction subtile.
Je n'ai toujours pas de clarté à 100%, mais voici ce qui m'a bien aidé:
function isAddingRole() {
return !('role' in resource.data) && 'role' in request.resource.data;
}
function isChangingRole() {
return 'role' in resource.data && 'role' in request.resource.data && resource.data.role != request.resource.data.role;
}
function isEditingRole() {
return isAddingRole() || isChangingRole();
}
Une autre chose qui me surprend encore est que, selon la documentation, je n’aurais pas besoin de la partie && 'role' in request.resource.data
dans isChangingRole()
, car elle devrait être insérée automatiquement par Firestore. Bien que cela ne semble pas être le cas, sa suppression entraîne l'échec de mon écriture pour des problèmes d'autorisations.
Il pourrait probablement être clarifié/amélioré en divisant l'écriture en parties create
, update
et delete
, au lieu de simplement allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
.
Je l'ai résolu en utilisant writeFields
. S'il vous plaît essayez cette règle.
allow write: if !('role' in request.writeFields);
Dans mon cas, j'utilise list
pour restreindre la mise à jour des champs. Cela fonctionne aussi.
allow update: if !(['leader', '_created'] in request.writeFields);
request.resource.keys.hasAny () est probablement votre meilleur pari ici. Il vous permet de vérifier si une requête donnée a l'une des clés spécifiées. En renonçant à la logique, vous pouvez vous assurer que les clés d’écriture des demandes d’écriture ne figurent pas sur la liste noire. Par exemple:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
//read rules here...
allow write: if !request.resource.data.keys().hasAny(["role", "adminOnlyAttribute"]);
}
}
}
Avec cette fonction unique, vous pouvez vérifier si des champs sont/ne sont pas créés/modifiés.
function incomingDataHasFields(fields) {
return ((
request.writeFields == null
&& request.resource.data.keys().hasAll(fields)
) || (
request.writeFields != null
&& request.writeFields.hasAll(fields)
));
}
Usage:
match /xxx/{xxx} {
allow create:
if incomingDataHasFields(['foo']) // allow creating a document that contains 'foo' field
&& !incomingDataHasFields(['bar', 'baz']); // but don't allow 'bar' and 'baz' fields to be created
Depuis que la référence à writeFields dans la documentation a disparu, je devais trouver une nouvelle façon de faire ce que nous pouvions faire avec writeFields.
function isSameProperty(request, resource, key) {
return request.resource.data[key] == resource.data[key]
}
match /myCollection/{id} {
// before version !request.writeFields.hasAny(['property1','property2','property3', 'property4']);
allow update: isSameProperty(request, resource, 'property1')
&& isSameProperty(request, resource, 'property2')
&& isSameProperty(request, resource, 'property3')
&& isSameProperty(request, resource, 'property4')
}
Pour appliquer les champs en lecture seule du client, utilisez writeFields
( https://firebase.google.com/docs/reference/rules/rules.firestore.Request#writeFields ). Gekijin a suggéré cela, mais la syntaxe a été légèrement erronée.
match /myCollection/{id}
// Readonly fields
allow update: if !(request.writeFields.hasAny(['field1', 'field2']));
}
Firestore peuplera les writeFields pour vous, de sorte que la vérification ci-dessus peut toujours être effectuée en toute sécurité dans vos règles update
& create
.
EDIT 9 oct 2018
@ dls101 a eu la gentillesse d'informer que Google semble avoir supprimé toute mention de writeFields
de la documentation. Alors soyez prudent en utilisant cette solution.