0
votes

I'm trying to create a rule that only lets a user create/edit a document in which request.auth.uid matches the name of the document they're trying to create. From https://cloud.google.com/firestore/docs/security/secure-data:

Using variables from wildcards

When you match a rule using a database path with a wildcard, a variable is created with a name matching the string you included inside the curly brackets. You can then reference these variables in your security rules.

One common use of these types of wildcards is to store documents by userID, and then only allow users to access documents for which their userID matches the ID of the document.

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userIDFromWildcard}/ {
      // Users can only edit documents in the database if the documentID is
      // equal to their userID
      allow read, write: if request.auth.uid == userIDFromWildcard;
    }
  }
}

I tried using this code in my rules, but it errors. When removing the last /, i.e. match /users/{userIDFromWildcard} instead of match /users/{userIDFromWildcard}/, it publishes, but permissions aren't granted if the document doesn't exist.

Edit: I also tried something like this, but I don't know how to do it correctly because of the lack of documentation:

allow read, write: if request.auth.uid == request.resource.__name__;

This is the code I'm running that gets insufficient permissions:

this.items = afs.doc("/users/" + this.uid); // afs is AngularFirestore, this.uid is the uid of the authenticated user

...

// the user is authenticated at this point
this.items.snapshotChanges().subscribe(snapshot => {
if (!snapshot.payload.exists) { // if user isn't in db
    function recursive() {
        // ...
        this.updateDB(); // called once because of conditional
    }
    recursive();
    } else {
        // ...
    }
}

updateDB:

public updateDatabase(): void {
    const changes: Item = { name: this.name, username: this.username, 
    photoURL: this.photoURL };
    this.items.set(changes).then((item) => { // causes error
        console.log("Saving successful");
    }, (err) => {
        console.log(err);
    }).catch((err) => {
        console.log(err);
    });
}
1
Can you show the code you're running that produces the error and what the error looks like? - Sam Stern
@hatboysam There's no error, it simply isn't working. Unfortunately, firestore rules don't have a simulator or decent debugging tools. The code is in the question, a direct copy/paste of the code in the quote except without the last slash - Cilan
those are only your rules, what about the code that actually calls set() or update() to write the data? And that's the code that should produce some error. - Sam Stern
@hatboysam Oh yes, my question is not detailed enough. It will be updated shortly - Cilan

1 Answers

0
votes

You probably need this setup for documents:

service cloud.firestore {
  match /database/{databases}/documents {
    match /users/{userId}/{document=**} {
      allow read, write: if request.auth.uid == userId;
    }
  }
}

Also, if you need to write to a sub-collections see this answer

And more info on wildcard matching in firestore here