allow user to update only one field - firebase-storage

is there a simple way to allow the update of a single field of the document without comparing all the document fields ?

You can check what fields are affected by a write operation, and then restrict based on that. From the notes when map diffs were introduced:
// This rule only allows updates where "a" is the only field affected
allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(["a"]);

Related

Firestore security rule to restrict writes to fields for both set and update?

I'm trying to write some Firestore security rules that only allow users to write to certain fields in their documents (e.g. email, gender, preferredName, address).
I wrote the following write rule to restrict access to specific fields:
match /databases/{database}/documents {
match /users/{userId} {
allow read: if userIsAuthenticated()
&& userIsAccessingTheirData(userId);
// Users can always write to specific fields
allow write: if userIsAuthenticated()
&& userIsAccessingTheirData(userId)
&& request.resource.data.keys().hasOnly(["preferredName","gender", "email", "address"]);
The rules works well for when we call userDoc.set in code, but it doesn't work when we call userDoc.update.
Using the Firestore rules emulator, I can see that when we call "set" the request.resource.data.keys() only has the fields that are being passed in the call, but when I call "update" all the fields of the document are in the key collection :-( which makes it impossible to filter.
Is there a way to write a security rule that restricts the fields like above that works for both set and update?
The request.resource variable represents the document as it will exist after the operation succeeds (if it succeeds of course). So request.resource does not just contain the fields that are being updated, but also the other values from the existing document.
It's always been possible to check if a field is being updated by comparing request.resource.data.fieldname with resource.data.fieldname.
But recently a new affectedKeys() function was introduced to security rules that shows just the delta:
// This rule only allows updates where "a" is the only field affected
allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(["a"]);
Also see the release notes for Firebase security rules.
you can access document fields using request.resource.data.{field}
for example if you want to restrict updating dob :
service cloud.firestore {
match /databases/{database}/documents {
// Make sure all cities have a positive population and
// the name is not changed
match /users/{user} {
allow update: if request.resource.data.dob == resource.data.dob;
}
}
}
This means that the document can be updated as long as dob hasn't changed which is what we are trying to achieve.

Firestore collection group rules

I have few questions about collection group:
Is there way to execute request for collection group in Firestore simulator?
Can I add additional parameter for collection group rules for example the following rule is used for collection group
match /{prefix=**}/access/{email} {
allow read: if isSignedIn();
}
before access collection i have one more collection with user id, is is possible to add it as parameter to do some validations?
No, there currently is no way to simulate a collection group query in the Firestore console. (Actually there is no querying at all except individual document gets.)
There is no way, using security rules, to know any of the other path elements that come before access in the case you're showing. The prefix wildcard actually will not even contain any data at the time of execution.

Can you predefine allowed fields on documents in Meteor/MongoDB?

I searched everywhere for this but I can't find anything on that matter. If you don't predefine fields in MongoDB, couldn't a user with Insert permission then Insert a document with every kind of field he wants? Via Collection.insert? If I am thinking correctly here, is there a way to restrict this?
You can restrict inserting any kind of fields in these two ways:
Use collection.allow/deny(http://docs.meteor.com/#/full/allow) - the insert callback has a doc parameter, which contains the exact document that user wants to insert - you can check the content of it and deny the insertion if you spot fields that are not allowed.
Use SimpleSchema (https://github.com/aldeed/meteor-simple-schema) and Collection2 (https://github.com/aldeed/meteor-collection2) packages to define the schema and attach it to your collection - it will prevent the insertion if a document has additional/missing fields (or fields of not expected type).
This is my personal preference. Because fieldNames param in
Collections.update(userId, doc, fieldNames) only gives top-level fields in doc. So if you are having nested fields it is very hard to track.
So I don't use collection allow/deny rules. Without allow deny rules Collections.insert/Collections.update does nothing on client. Instead I am using Meteor methods to update/delete documents to collections, so I can decide which exact fields should update/insert.

MongoDB: how to persist selected document from collection

I'm new to MongoDB and I'm not how to best solve my fairly basic problem.
I have a Collection of "emoji" Documents in my database. At any given time, there is one (and only one) "selected" emoji Document. This is determined and updated by the application. How can I persist the information of which one is selected to the database?
Approach 1:
Add a new Collection to hold this kind of metadata of the emoji collection? I'm thinking it would hold a single document with a reference to the currently selected emoji document. This seems to hurt the OO design. A whole collection, with a single document, to hold a single property. But it does have flexibility to add more metadata.
Approach 2:
Add a new boolean field to each emoji Document indicating whether or not it is the current selected emoji. This seems like a lot of extra info to track for each Document, when only one should have a true value. I would also be concerned with maintaining consistency.
I know I'm not the first person to have this issue, but I couldn't find a solution this is as a general case. Thanks!
MongoDB is schemaless so you can just add the boolean field to the currently selected emoji and remove it when the selection changes. You should add a parse unique index to make querying this field faster. You could set the field using this syntax:
db.emojis.update({name:"b"},{$set:{selected:true}})
And simply unset it like this:
db.emojis.update({name:"b"},{$unset:{selected:""}})
You could create the following parse unique index to ensure there is only ever one field with selected:true
db.emojis.createIndex( { selected: 1 } , { sparse: true, unique: true } )

The fastest way to show Documents with certain property first in MongoDB

I have collections with huge amount of Documents on which I need to do custom search with various different queries.
Each Document have boolean property. Let's call it "isInTop".
I need to show Documents which have this property first in all queries.
Yes. I can easy do sort in this field like:
.sort( { isInTop: -1 } );
And create proper index with field "isInTop" as last field in it. But this will be work slowly, as indexes in mongo works best with unique fields.
So is there is solution to show Documents with field "isInTop" on top of each query?
I see two solutions here.
First: set Documents wich need to be in top the _id from "future". As you know, ObjectId contains timestamp. So I can create ObjectId with timestamp from future and use natural order
Second: create separate collection for Ducuments wich need to be in top. And do queries in it first.
Is there is any other solutions for this problem? Which will work fater?
UPDATE
I have done this issue with sorting on custom field which represent rank.
Using the _id field trick you mention has the problem that at some point in time you will reach the special time, and you can't change the _id field (without inserting a new document and removing the old one).
Creating a special collection which just holds the ones you care about is probably the best option. It gives you the ability to logically (and to some extent, physically) separate the documents.
Newly introduced in mongodb there is also support for a "sparse" index which may fulfill your needs as well. You could only set the "isInTop" field when you want it to be special, and then create a sparse index on it which would not have the problems you would normally have with a single indexed boolean field (in btrees).