Abraham Hamidi
Abraham Hamidi

Reputation: 13789

Firestore rules when data doesn't exist

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);
    });
}

Upvotes: 0

Views: 1739

Answers (1)

Alex
Alex

Reputation: 181

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

Upvotes: 1

Related Questions