I have the following firestore rules setup where I want to let anyone create a document in the suggestions collection, but only a certain user can update it. The rules look like this:
service cloud.firestore {
match /databases/{database}/documents {
match /suggestions/{sugg} {
allow create, read;
allow update: if request.auth.uid == 'abc123';
}
}
}
When I test this in the provided simulator, it works fine; however after waiting 30 minutes I test in my deployed app and I get the error:
Error: Missing or insufficient permissions.
In my app I'm doing an add() call on the suggestions collection. So in my rules where I specify allow create, that should be sufficient. I added read in case the returned document would count as a read.
Client: (AngularFire)
this.afs.collection('suggestions').add(sugg).then(() => {
this.submitted = true;
}, err => console.error('Firebase error:', err));
The issue was that Firestore couldn't try to match a uid when request.auth was null. The solution was to add some functions:
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function isAdmin() {
return isSignedIn() && request.auth.uid == 'abc123';
}
match /suggestions/{sugg} {
allow create, read;
allow update: if request.auth.uid == 'abc123';
}
}
}
Related
I have a collection in which I am storing user requests in documents having documents ID as user's email. In the document, I am creating fields the key for which is being generated at client side.
Now, the problem that I am facing is that user can overwrite the existing field/request in the document if the key matches which I don't want to happen.
What I tried was to use this rule which unfortunately does not work
resource.data.keys().hasAny(request.resource.data.key();
So how can I achieve this?
Below are the screen shot of the firestore data and the current security rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /roles/{userId}{
allow read: if isSignedIn() && hasId(userId);
}
match /requests/{email} {
allow read, update: if isSignedIn() && hasMail(email)
}
//functions//
function hasMail (email) {
return request.auth.token.email == email;
}
function hasId (userId) {
return request.auth.uid == userId;
}
function isSignedIn () {
return request.auth != null;
}
function getUserRole () {
return get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.role
}
}
}
You can check if a resource already exists. Here an example:
allow write: if resource == null // Can create, not update
Use that to restrict any edit or update of the data. If you have additional rules you can granulate them to update, delete and create.
I am having weird behaviors with my Firebase Firestore Rules recently.
I am protecting a collection like so:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function getRoles() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role;
}
function isAdmin() {
return 'admin' in getRoles();
}
match /registrationsRequests/{document} {
allow create: if true;
allow read, update, delete: if false;
}
match /users/{document} {
allow read: if request.auth.uid == document || isAdmin();
allow write: if request.auth.uid == document || isAdmin();
}
}
}
After I log in (and I check if the user is available with its UID), I fetch the user. But I receive the following error (both checks should return true => isAdmin() and document)
FirebaseError: Missing or insufficient permissions.
#edit: The query used
const usrDoc = await this.db.collection('users').doc(user.uid).ref.get()
I'm trying to restrict access to a collection based on a users "role" on the document as well as whether they're signed in.
Just checking if they are signed in works:
rules_version = '2';
function isSignedIn() {
return request.auth != null;
}
service cloud.firestore {
match /databases/{database}/documents {
match /workspaces/{workspace} {
allow read: if isSignedIn();
}
}
}
But I want to make it a bit more granular, I'm following this Google guide. When I configure it exactly the same my frontend client errors with FirebaseError: Missing or insufficient permissions. However, testing the rule in the Firebase portal works(?).
rules_version = '2';
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.access[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
service cloud.firestore {
match /databases/{database}/documents {
match /workspaces/{workspace} {
allow read: if isOneOfRoles(resource, ["owner", "viewer"]);
}
}
}
As mentioned, testing the rule via Firebase works, however when querying from my frontend app it fails.
// VueJS
let workspace = []
async fetchWorkspaces() {
const db = firebase.firestore();
await db.collection('workspaces').get().then(response => {
response.forEach(snapshot => {
this.workspaces.push(snapshot.data());
})
})
}
Database document
I've also tried storing the RBAC for each user as a document in a subcollection and using the following rule
allow read: if isSignedIn() && exists(/databases/$(database)/documents/workspaces/$(workspace)/users/$(request.auth.uid));
Still doesn't work. I can only seem to grant broad access (is signed in)
I need some help making my security rules for firestore work.
These are my firestore rules:
service cloud.firestore {
match /databases/{database}/documents {
match /orders/{orderID} {
allow read, update: if request.auth.uid == resource.data.buyerId || request.auth.uid == resource.data.sellerId;
}
}
}
my orders collection:
orders: {
sellerId: 'some-id',
createdAt: timestamp,
buyerId: 'some-id'
}
It should return all documents from orders collection which has either buyerId or sellerId equal to authorised user (request.auth.uid).
but the above rule is not working as expected.
firestore collections screenshot
firebase simulator output
That error message is suggesting that the requested document was not actually present in the database. You entered "orders/{orderId}", which looks like you put a wildcard in the Location field in the simulator. That's not going to work. You need to enter the path to an actual document that exists if you want to test your rule that uses its field values.
resource.data: Null - this error happens when you try to create a new entity.
Split write rule, on create and update.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /user/{userId} {
allow read: if request.auth.uid == userId;
function authed() {
return request.auth.uid == userId;
}
allow create: if authed() && request.resource.data.keys().hasOnly(['name']);
allow update: if authed() && request.resource.data.diff(resource.data).changedKeys().hasOnly(['name']);
allow delete: if authed();
}
}
}
I'm self studying firestore and I could not figure out a way to only allow a user to update, delete or read only the collections added by them.
This is the structure I'm using:
I use firebase auth for user handing. I save the currentUser.uid as user_id in the database for each collection.
These are the rules I'm using
service cloud.firestore {
match /databases/{database}/documents {
match /tasks{
allow read, update, delete: if request.auth.uid == resource.data.user_id;
allow create: if request.auth.uid != null;
}
}
When I try to read/get the data I get Missing or insufficient permissions error.
I'm using the web api (JavaScript) for firestore. This is the code I'm using to read data.
function read() {
db.collection("tasks").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var newLI = document.createElement('li');
newLI.appendChild(document.createTextNode(doc.data().task));
dataList.appendChild(newLI);
});
});
}
the error was in my JavaScript I was getting all without filtering by user
function read() {
let taskColletion = db.collection("tasks");
taskColletion.where("user_id", "==", firebase.auth().currentUser.uid).get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var newLI = document.createElement('li');
newLI.appendChild(document.createTextNode(doc.data().task));
dataList.appendChild(newLI);
});
});
}
This is actually explained on the Firestore Documentation(I recommend reading it).
You're missing a wildcard after /tasks:
service cloud.firestore {
match /databases/{database}/documents {
match /tasks/{task} {
allow read, update, delete: if request.auth.uid == resource.data.user_id;
allow create: if request.auth.uid != null;
}
}
}