How to filter fields in documents with security rules - firebase-security

I am experimenting with Cloud Firestore security rules. Is it possible to filter document fields?
For example if you have a document
{
name: "John Doe",
email: "doe#example.com"
}
then some users aren't allowed to get the document with the email address. Their application requests the document with
firebase.firestore.doc('users/doe-uid')
and gets this document
{
name: "John Doe",
}
If yes, how?
I think it should be possible because the Cloud Firestore Security Rules Reference says in the first sentence (emphasis is mine):
Cloud Firestore Security Rules are used to determine who has read and write access to collections and documents stored in Cloud Firestore, as well as how documents are structured and what fields and values they contain.
However I couldn't find anything in the reference telling me how to filter out fields.

Firestore rules are not filters, they're a server-side validation of document queries, meaning that you access (or not) the whole document, not particular fields.
The piece of documentation you mentionned means that you can do data validation on fields.
Here is a basic example of rules validating data on a write query (via request.resource.data) :
match /users/{userId} {
allow write: if request.resource.data.age is int;
}
Here is another basic example that uses an existing field to validate a read query (via resource.data) :
match /articles/{articleId} {
allow read: if resource.data.isPublished == true;
}
To filter out fields, you have do it client side, after the query.
Now If you want to secure access to certain fields, you have to create another collection (look into subcollections) with a different set of rules, and make another query that will match these rules.

Related

REST GET mehod: Can return a list of enriched resources?

I have a doubt when I'm designing a REST API.
Consider I have a Resource "Customer" with two elements in my server, like this:
[
{
name : "Mary",
description : "An imaginary woman very tall."
},
{
name : "John",
description : "Just a guy."
}
]
And I want to make an endpoint, that will accept a GET request with a query. The query will provide a parameter with a value that will make an algorithm count how many occurrences for this text are there in all of its parameters.
So if we throw this request:
GET {baseURL}/customers?letters=ry
I should get something like
[
{
name : "Mary",
description : "An imaginary woman very tall.",
count : 3
},
{
name : "John",
description : "Just a guy.",
count : 0
}
]
Count parameter can not be included in the resource scheme as will depend on the value provided in the query, so the response objects have to be enriched.
I'm not getting a list of my resource but a modified resource.
Although it keeps the idempotent condition for GET Method, I see it escapes from the REST architecture concept (even the REST beyond CRUD).
Is it still a valid endpoint in a RESTful API? or should I create something like a new resource called "ratedCustomer"?
REST GET mehod: Can return a list of enriched resources?
TL;DR: yes.
Longer answer...
A successful GET request returns a representation of a single resource, identified by the request-target.
The fact that the information used to create the representation of the resource comes from multiple entities in your domain model, or multiple rows in your database, or from reports produced by other services... these are all implementation details. The HTTP transfer of documents over a network application doesn't care.
That also means that we can have multiple resources that include the same information in their representations. Think "pages in wikipedia" that duplicate each others' information.
Resource identifiers on the web are semantically opaque. All three of these identifiers are understood to be different resources
/A
/A?enriched
/B
We human beings looking at these identifiers might expect /A?enriched to be semantically closer to /A than /B, but the machines don't make that assumption.
It's perfectly reasonable for /A?enriched to produce representations using a different schema, or even a different content-type (as far as the HTTP application is concerned, it's perfectly reasonable that /A be an HTML document and /A?enriched be an image).
Because the machines don't care, you've got additional degrees of freedom in how you design both you resources and your resource identifiers, which you can use to enjoy additional benefits, including designing a model that's easy to implement, or easy to document, or easy to interface with, or easy to monitor, or ....
Design is what we do to get more of what we want than we would get by just doing it.

Zapier lazy load input fields choices

I'm building a Zapier app for a platform that have dynamic fields. I have an API that returns the list of fields for one of my resource (for example) :
[
{ name: "First Name", key: "first_name", type: "String" },
{ name: "Civility", key: "civility", type: "Multiple" }
]
I build my action's inputFields based on this API :
create: {
[...],
operation: {
inputFields: [
fetchFields()
],
[...]
},
}
The API returns type that are list of values (i.e : Civility), but to get these values I have to make another API call.
For now, what I have done is in my fetchFields function, each time I encounter a type: "Multiple", I do another API call to get the possible values and set it as choices in my input field. However this is expensive and the page on Zapier takes too much time to display the fields.
I tried to use the z.dehydrate feature provided by Zapier but it doesn't work for input choices.
I can't use a dynamic dropdown here as I can't pass the key of the field possible value I'm looking for. For example, to get back the possible values for Civility, I'll need to pass the civility key to my API.
What are the options in this case?
David here, from the Zapier Platform team.
Thanks for writing in! I think what you're doing is possible, but I'm also not 100% that I understand what you're asking.
You can have multiple API calls in the function (which it sounds like you are). In the end, the function should return an array of Field objects (as descried here).
The key thing you might not be aware of is that subsequent steps have access to a partially-filled bundle.inputData, so you can have a first function that gets field options and allows a user to select something, then a second function that runs and pulls in fields based on that choice.
Otherwise, I think a function that does 2 api calls (one to fetch the field types and one to turn them into Zapier field objects) is the best bet.
If this didn't answer your question, feel free to email partners#zapier.com or join the slack org (linked at the bottom of the readme) and we'll try to solve it there.

Firestore Database Rules for User

I'm following a tutorial about firestore but I don't understand firestore rules very well. I'm trying to allow anyone to be able to create in the standard
users/uid/
path but only allow updates if the requester is trying to update
users/theirUserId/
I saw this in the documentation, but it didn't seem to work for me:
allow write: if request.auth.uid == resource.data.author_id;
Can anyone explain the functionality of the above line and/or offer suggestions as to how I can achieve this?
Additionally, is there any way to specify rules for a specific piece of data within a document?
It looks like that your document doesn't contain a author_id field.
The Firebase documentation Writing Conditions for Security Rules use this example:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches the 'author_id' field
// of the document
match /users/{user} {
allow read, write: if request.auth.uid == resource.data.author_id;
}
}
}
It means that a random user will be able to read and write in the users collections only if their authentication ID equals the author_id field of a specific document.
The resource variable refers to the requested document, and resource.data is a map of all of the fields and values stored in the document. For more information on the resource variable, see the reference documentation.
For your second question, I recommend you to have a look on the documentation about resource variable (link in the quote above). It is the same logic as your author_id question.
You can split allow write in to three create, update, delete for specific cases.
In your case
allow create: if request.auth.uid != null;
allow update: if request.auth.uid == resource.data.author_id;
which says any authenticated users can create and only update their on document. and created user must have a field author_id which is their user id.

Firebase simple blog (confused with security rules)

I'm trying to create a simple todo or blog system based on React + ReactFire.
And after a hour of reading firebase tutorial confused about configuring firebase security rules.
Code for saving element :
this.props.itemsStore.push({
text : this.state.text,
done : false,
user : this.props.user.uid
})
Everything ok, but how i can get all records what owns only but authorized user?
This rules doesn't works :
"rules": {
"items" : {
".write" : "auth !== null",
"$item" : {
".read": "data.child('user').val() == auth.uid"
}
}
}
Seems to there no way to get all records only for one user, with security rules, instead of this, i should use something like filter. But again, i don't know how to filter elements in ReactFire, and in manuals no information.
As example how does it work in Parse http://i.stack.imgur.com/l9iXM.png
The Firebase security model has two common pitfalls:
permissions cascade: once you've granted a read or write permission on a specific level, you cannot take this permission away at a lower level
rules are not filters: (this is essentially a consequence of the previous pitfall) you cannot use security rules to return a different subset of children for specific users. Either a user has access to a node, or they don't have access to it.
You seem to be falling for that second pitfall. While the user can access each specific message that they are the user for, they cannot query the higher-level items node since they don't have read access to it.
If you want to secure a list of messages/todos for a specific user, you will need to store that data for that specific user.
items_per_user
$uid
$itemid: true
This is quite common in NoSQL database and is often called denormalizing. See this article called "denormalization is normal" on the Firebase web site. It's a bit outdated as far as the Firebase API goes, but the architectural principles on denormalizing still apply.
To then show the items for a user, you'd do:
ref.child('items_per_user')
.child(ref.getAuth().uid)
.on('child_added', function(snapshot) {
ref.child('items')
.child(itemId.key())
.once('value', function(itemSnapshot) {
console.log(itemSnapshot.val());
});
})
Many developer new to Firebase think that the inner loop will be too slow to load their data. But Firebase is very efficient when it comes to handling multiple requests, since it only opens a connection once per client and pipelines all the requests in the inner loop.
Keep in mind, Rules are not filters. They allow access to nodes based on criteria.
Here's an example simple structure where users 0 and 1 have stored text data within their node.
Data Structure
ToDo
a_user_id_0
text: "some text"
done: yes
a_user_id_1
text: "another text"
done: no
Rules
In this example rule, users can only read/write from nodes that belong to them within the ToDo node, so the path $user_id would be equal to their auth.id. It assumes the users has authenticated as well.
"ToDo": {
"$user_id": {
".read": "auth != null && $user_id == auth.uid",
".write": "auth != null && $user_id == auth.uid"
}
}
If user_0 was auth'd and attempted to read/write data from a_user_id_1 node, it would fail.

RESTful web service: create new resource by combining other resources: provide IDs or URIs?

When obtaining a collection of items from a RESTful web service (via GET), the representation of each single item (e.g. in JSON) usually contains the item's resource identifier. This can either be the ID of the resource or the entire URI which usually contains the ID.
This identifier (ID or URI) is required in case the client needs to further interact with the remote resource representing the single item. Many people seem to consider it good practice to provide the entire URI and not only the ID, so that the client has nothing to do with URI construction (for example, this is what Miguel Grinberg writes in this article).
But what should be done in case multiple items are to be combined in order to create a new resource? Then the client needs to tell the server which items are to be combined. And eventually, the server requires a list of IDs for processing the request. Assuming that the client retrieved URIs for each item in the first place -- where would you perform the URI parsing in order to extract the raw IDs again: in the client or in the server?
Example: the client retrieved a collection of pages in a GET request. Each page item identifies itself with an URI (containing the ID):
{
"pages": [
{
"content": "bla bla",
"uri": "/pages/1"
},
{
"content": "that is no interesting content",
"uri": "/pages/2"
},
...
]
}
Now assume that the client instructs the server to create a new resource combining multiple pages: a book, built by pages 1 and 2. The POST request body can either contain IDs or URIs:
{
"title": "A Boring Book",
"pages": [1, 2]
}
or
{
"title": "A Boring Book",
"pages": ["/pages/1", "/pages/2"]
}
In the first case, the clients needs to know the structure of the URI and extract the ID before sending the request. In the second case the server needs to extract the ID from the URI.
On the one hand, I like the idea of resources being represented on the client side by URIs only. On the other hand, I also like to keep things simple and pragmatic and why should we send entire URIs to the server when the context is clear and only the IDs are needed (the book creation does not directly act on page resources)?
What would you prefer and why? Or do you think that this is really not too important?
Do you think the following approach would be a good compromise? Client-side extraction of the ID from the URI by parsing the URI from right to left and extracting the number after the rightmost slash, i.e. assuming a certain URI structure without the need to hardcode the entire path.
I think that clients should receive absolute URLs from the server and only use these without any kind of modification. Therefore, I would even go one step further beyond your last example:
{
"title" : "A Boring Book",
"pages" : [ "http://.../pages/1", "http://.../pages/2" ]
}
Only the server should be responsible to extract Ids from URLs if necessary.