Firestore Security rule always returns null for resource - google-cloud-firestore

I am trying to create some firestore security rules. However, every rule that I write that involves something other than the users database pulling the document of the current user results in an error. There is some difference I am missing.
Here is the query and the data. The resource object is always null. Any get function that involves pulling from the design database using the designId variable also results in null.

You're putting a pattern into the form, which is not valid. You need to provide the specific document that you want to simulate a read or write. This means you need to copy the ID of the document into that field. It should be something like "/designs/j8R...Lkh", except you provide the actual value.

Related

Firestore Rule - limiting "list" access

I have a collection on which I want to provide list access, but only in a limited manner for most users.
All users should be able to do this: (the string valuex can be anything)
collection("XYZ").where("fieldx", "==", "valuex").get()
Only admins can get all the documents:
collection("XYZ").get()
Note that as valuex can be anything, at the end of the day all users can see all documents. The difference is that non-admins need to know what to query, admins don't, they get it all directly.
The only solution I have found is to force non-admins to write to a document the value they are querying, prior to calling get. The rules then are:
allow list: if isadmin() || resource.data.fieldx == getvaluex();
function isadmin() { return request.auth.token.get("admin", false); }
function getvaluex() { return get(/databases/$(database)/documents/users/$(request.auth.uid).data.valuex; }
That way all returned documents must have the same value for fieldx. But this solution 1) needs 1 additional write 2) adds a read in the rules and 3) in my case valuex is sensitive and I dont want the user to have to store it in Firestore.
So is there any better solution?
Is it possible for instance to limit the usage of an index to only some users? (both queries above actually have more where statements and require each a specific composite index).
Is it possible to compare the returned documents between each others to ensure they all have the same value for fieldx?
The way I would do it is this:
Don't allow non-admins to make those direct requests to the database at all.
Instead, have them send a request to a Firebase Http function.
The Http function has admin access to the db, it can accept any valuex non-null value.
It queries the db using that valuex, on behalf of the non-admin users, and returns the results.
This way, you can keep the documents in collection XYZ locked to non-admins in your Firestore Rules.
You can even keep sensitive data in those documents, since you have control on what you share with users. You can control that by choosing which fields your HTTP function will return to clients.
Mind you, Firebase function invocations are way cheaper than making additional writes/reads.
Firestore works well for easy/normalized access from clients to collections and documents.
What you are trying to do is pretty specific to your implementation of the these "lists".
You may create another collection (list_auth) that tracks the accesses to the list.
In the security access you can create a security rule for the collection that looks up the permissions of user into the list by accessing the list_auth collection.
https://firebase.google.com/docs/firestore/security/rules-conditions#access_other_documents

Firestore security rules based on individual documents?

In my Google Firestore collection, I have a series of documents. Each of these documents have their own data. Included in each of these documents is a field called api_key;
Using Javascript, how would I make it so that a set/update command can only be accepted if the command properly includes the value in api_key. Using the api_key, the script should be able to set/update any content within that document tree... and only that document tree.
Is something like this possible?
I don't believe this is possible currently (without using a deprecated expression, which is strongly discouraged), because security rules don't allow you to distinguish between a value provided for a field in a document update, and the value of an existing field with the same name. It's tempting to try this to try to compare the provided value with the existing value:
allow update: if "apiKey" in request.resource.data
&& request.resource.data.apiKey == resource.data.apiKey;
But when you say request.resource.data.apiKey, that will evaluate to either the existing field value in the document, or the provided value. So, if someone simply didn't provide apiKey in the update, the security rule would just provide the existing apiKey value, and the write would be allowed. This rule would just reject writes where the apiKey is provided by doesn't match the existing value.

Return all fields from API endpoint

I might be blind and just not seeing it in the documentation, but is there a parameter that I can pass in that will return all the fields available in an endpoint instead of having to list out all of the fields?
I looked for the same thing when I started working with the API and I don't believe it exists (if it does it's not documented). Whatever fields you explicitly call for are the ones that you get back (if you call for none you get ID and ETAG only).
Whatever solution you come up with for your application, one thing to keep in mind is that if you include a field in the call which doesn't exist on the model, e.g. ask for a field called document on a matter, the entire call will return nothing. So, you can't, for instance, just request all the fields from the entire API, which would be convenient, but you could build up a list of all the fields which occur on a particular subsection. I ended up doing something like that for subfields (which are also not returned unless specifically asked for, which is annoying).
All you need to do is ?fields= at the end for return all fields
?fields= returns all with no value needed for a wild card
?fields= returns all with no value needed for a wild card

Add subcategories in a filtered API Restful resource

I'll give an example as the title might sound a bit confusing.
How to build a resource path for something like that:
GET /courses/?language=english&active=true/units
I want to filter the courses (not using an id as usually) and then get the units of this result. How would you do that? I guess using question marks between the path is not allowed.
That would depend a little on your DB schema of what is a "course" and a "unit". The whole point on using the RESTful way is to always build requests and urls resource-specific.
But let's say that one course has X units on it. Here's what i would do to make a RESTful path to that request:
Due to the path problem of filtering courses AND using the /unit suffix, it can be done by adding another query parameter that specifies what fields the request is supposed to return. Something like this:
GET /courses?language=english&active=true&fields=units
That would filter the courses, and then return only the 'units' field on the response. As i said, depending on your DB and models, if the units are not stored inside the courses, it would be a bad practice to get them by requesting a /courses path. In that case, first request the courses that match the desired filter, and then make another request to the /units context sending i.e the courses ID's as query parameters.

simple model when requesting collection and extended model when requesting resource - how

I have the following URI: /articles/:id, where article is a resource on web-service and have associated model/class. Now I need to return only partial data for each resource (to save bandwidth and make for speed) when collection is requested, but when a single item is requested from collection I need to send full data. My question is should I use two models/classes for the same resource on the server and initiate different one depending on collection or single resource is requested? Or maybe there is should be only one model/class but not all fields should be filled with data when a collection is requested? Or maybe there is another approach?
I suggest using the approach suggested here with a fields query parameter.
If the API is going to be open to everyone to use and client usage is going to be unpredictable, then by default you probably need to limit the fields that you return. Just make sure you document in some way all the possible fields that could be used, in case a client actually needs them.
If the API is going to be consumed only by an app or apps you made, then by default you could return all of the fields and then your app can pass that fields parameter to speed things up.