Firestore rules not working for userId - firebase

I'm having a simple collection users, document id is using the uid with name string
I'm using angularfire2 to connect to the collection
this.users = afs.collection<User>('users').valueChanges();
I'm using firebase authentication. When displaying the user id, I get 4NxoeUeB4OXverhHhiw86eKl0Sj1 which match with the user document.
this.afAuth.authState.subscribe(auth => console.log(auth.uid));
I'm trying to add rules for the collection. If I use the rules below, I get error Missing or insufficient permissions.
`service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}
}
}`
If I change the rule to request.auth != null the data is shown.
`service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null;
}
}
}`
What could I have done wrong? I'm also trying a few different rules eg. resource.data.name != null. It is also not working.
I am testing the rule. The end result I want a readers array field and writers array field so I can try something like request.auth.uid in resource.data.readers to control access to the document

You are trying to get /users/* but you only have access to /users/{yourid}.
You should only retrieve data you have access to, so you need to filter your query even if you actually only have one document.
// either by adding a field in your doc
afs.collection('users', ref => ref.where('id', '==', auth.uid))
// or by getting the document directly
afs.collection('users').doc(auth.uid)
When performing a rules check on a query, Cloud Firestore Security
Rules will check to ensure that the user has access to all results
before executing the query. If a query could return results a user
doesn't have access to, the entire query fails and Firestore returns
an error.
Source : https://cloud.google.com/firestore/docs/security/secure-data#shallow_queries

Related

Firebase query, rule for collectiongroup query getting in the way

Just when I thought I had the hang of it, the query rules throws me a curve ball :(
I have this query rule:
// Needed for collection group (Member) query
// https://firebase.googleblog.com/2019/06/understanding-collection-group-queries.html
match /{rootPath=**}/Members/{member} {
allow read: if request.auth != null;
}
It's pretty basic, only needs an authorized user. collectiongroup query works perfectly as expected.
Now, I want to have another query just to get member documents:
Firebase.firestore.collection("Companies\\$companyID\\Members").get().await()
The query returns an error (PERMISSION_DENIED).
I also tried adding a rule just for members like this:
match /Companies/{companyID} {
allow read: if request.auth != null &&
isMember(database, companyID, request.auth.uid)
match /Members/{member} {
allow read: if request.auth != null
}
}
Still, the same error.
This is the document path:
I looked at a few resources, but I didn't see anything to suggest a solution:
Understanding Collection Group Queries in Cloud Firestore
Recursive wildcards
I am posting this as an answer, as it is too long for comment.
Have you tried the following example rule:
service cloud.firestore {
match /databases/{database}/documents {
match /Companies/{companyID}/Members/{member} {
allow read, write: if <condition>;
}
}
}
as mentioned earlier in the documentation you shared based on structuring rules with hierarchical data?
I would recommend that you have a look at the following documentation where you can find some examples:
service cloud.firestore {
match /databases/{database}/documents {
match /Companies/{companyID}/Members/{memberID} {
// Only authenticated users can read
allow read: if request.auth != null;
}
}
}
Using the above security rules, any authenticated user can retrieve the members of any single company:
db.collection("Companies/{companyID}/Members").get()
Now , if you would like to have the same security rules applied to collection group queries, you must use version 2:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/Members/{memberID} {
allow read: if request.auth != null;
}
}
}
Any authenticated user can retrieve the members of any single company:
But what if you want to show a certain user their members across all companies? You can use a collection group query to retrieve results from all members collections:
var user = firebase.auth().currentUser;
db.collectionGroup("members").where("author", "==", user.uid).get()
Note: This query requires will require a composite index for the members collection. If you haven't enabled this index, the query will return an error link you can follow to create the required index.
You can try using "match /{path=**}/Members/{member}" instead of rootPath. I have not used the latter but the former worked for me in other projects.

Access userId without having it as a field

I'm writing Firestore security rules for my project. I want to allow users to edit information in their own user page, but not in anyone else's. Right now I don't save userId as a field in each user, only as the reference to the user document. I know how to access fields in each user, but not the reference to them. See picture:
match /Users/{document} {
allow update: if request.auth.uid == userId; //how do I reach the userId without having it as a field
}
I do not want to add userId as a field in each user, there must be an easy way of accessing the path.
As mentioned in the Firestore docs you get the document id from the match query.
In your case this would be document from match /Users/{document}. You could also rename this query to match /Users/{userId} to make it work.
Check the second example in the documentation on using authentication information in security rules:
Another common pattern is to make sure users can only read and write their own data:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
}
}
So in your case that'd be if request.auth.uid == document.

Firebase Security Rule with attribute in database

I need security rules for my firebase-app.
My Data looks like this
{
owner = "djskjfskdjf",
data = "some data"
}
Collection Path is /Data/
I want that users that are authenticated users can read and create documents in this collection. To edit or delete documents, I want that the uid in the field owner is the same as the request.auth.id.
According to firebase documentation this should work:
service cloud.firestore {
match /databases/{database}/documents {
match /Data/{document=**} {
allow read, create: if request.auth.uid != null;
allow delete, write: if request.auth.uid == request.resource.data.owner;
}
}
}
But when I try to update a field in simulation it gives me the error:
Error: simulator.rules line [5], column [51]. Property resource is undefined on object.
I hope you can help me with this problem.
If your request isn't sending the all the required fields you're checking in rules, then request.resource.data.owner wont work (like if you are just sending data but not owner with the request).
You should use the below style to match an existing object in the database. It won't matter if the request omits the owner info.
allow delete: if resource.data.owner == request.auth.uid;

Limiting permissions to the author not working in Firestore rules

I am attempting to move my Firebase rules from testing where every user could read and write every document to one where only the author can update or delete documents they create.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
allow update, delete: if request.auth != null && request.auth.uid == resource.data.author_uid;
allow create: if request.auth != null;
}
}
}
This set of rules is resulting in a 'Missing or insufficient permissions" error at Firebase init and if I attempt to delete a document. If I go with my original rules then everything works.
allow read, create: if request.auth != null
I do a standard firebase.initializeApp (but dont want to publish my keys here - lets just say the same initialize works with the basic rules and on three other firebase projects I have). The delete call is as follows and works with the simpler rule set as well but not the tighter rules above:
const decrement = firebase.firestore.FieldValue.increment(-1);
firestore.collection('story').doc(storyid).delete().then(function() {
firestore.collection('users').doc(getUserID()).update({
saves: decrement
});
})
(thank to Sam Stern on the FB team for guidance)
First, there was a mistake in the rules description. While request.auth.uid is defined by firebase the resource.data.author_id needs to be defined by the developer on each of their documents. In this case the 'story' document contains a 'creator' property that is the userid of the owner. So the correct variable would be resource.data.creator in the above rules.
In addition its possible to define the documentid as the userid, as is often the case when you are creating a 'user' object for each account in your firebase app. In this case the userId is the same as the documentId so the following rule would control update permissions that only allow the owner of that document to change it.
match /users/{userId} {
// In this scope the "userId" variable is equal to the documentId because of the wildcard declaration {userId}
allow update: if request.auth.uid == userId;
}

Configuring rules for Firestore so user only gets records they own

This is a followup to this question
Firestore permissions
I'm trying to set rules on my firestore
service cloud.firestore {
match /databases/{database}/documents {
match /analysis/{analysis} {
allow read, write: if request.auth.uid == resource.data.owner_uid;
}
}
}
My goal is
a. When doing a list operation only those documents belonging to a user are returned
b. only documents a user owns can be read or written by that user.
With the above configuration b. is accomplished.
how do I do accomplish a. ?
Remember that firestore rules are not filters, they're a server-side validation of your queries.
You should always make your queries match your rules, or else you'll get permission errors.
In your case you already made the rule to enforce reading/listing on user owned documents. Now you simply have to make the corresponding query with the right filters :
const userId = firebase.auth().currentUser.uid
db.collection("analysis").where("owner_uid", "==", userId)
Another thing.
With your current rules, your users won't be able to create a new document, only edit an existing one, here are the updated rules to allow that :
allow read: if request.auth.uid == resource.data.owner_uid;
allow write: if request.auth.uid == resource.data.owner_uid
|| request.auth.uid == request.resource.data.owner_uid;

Resources