Can you predefine allowed fields on documents in Meteor/MongoDB? - 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.

Related

allow user to update only one field

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"]);

Firestore security rules: check if array contains strings different from user's ID

I know how to check if an array contains a given string (as explained for example here). My requirement however is different: I have a document with an array updatedByHistoryArray written at server side that contains the history of the ids of all users who updated such a document, for example [id1, id2, ..., idn].
I would like to allow a delete operation for this document only if the latter has been updated exclusively by the user who wants to delete it.
So, for example, if a user with id24 wants to delete a document, the updatedByHistoryArray of this document has to be [id24, id24, ..., id24].
Is it possible to implement this requirement in the security rules of Firestore?
It sounds possible. Try using hasOnly() to see if the list field contains only a single user ID.
resource.data.updatedByHistoryArray.hasOnly([request.auth.uid])

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.

firestore security rules get subcollection document

I'm facing insufficient permissions for this firestore security check.
service cloud.firestore
{
match /databases/{database}/documents
{
match /events/{eventID}
{
allow read:
if get(/databases/$(database)/documents/events/$(eventID)/authorizations/$(request.auth.uid)).data.EVENT_READ == true;
}
}
}
the get document is hardcoded in the firestore database and the simulator returns true but for the actual query returns insufficient privileges.
I tested and moved the authorizations subcollection to the same level as users collection and it works. Am i missing out anything?
Additional testing: Reading the document directly does not result in insufficient privileges. I'm testing to see if it's an issue with listing but to my knowledge read should cover both get and list in firestore security rules.
Update: Listing seems to be the issue here. I tried to list the entire collection with only one document and it results in the insufficient privileges.
Works:
this.angularFirestore.collection('events').doc(eventID).valueChanges();
Doesn't work (updated):
this.angularFirestore.collection('events', query => query.where('admins', 'array-contains', auth.uid)).valueChanges()
My firestore database:
/events/event1_id
- field 1: some string
- field 2: some string
- admins: array of uid strings
/authorizations/<uid> #uid for $(request.auth.uid)
- EVENT_READ: true
Update 2: Updated the doesn't work query string which I tried out. It is intriguing that if i move the /authorizations sub collection out to be the same level as /events collections, the query will not fail.
Your first query works because it's accessing the events collection with a specific document. That specific document meets the rules criteria, because you've arranged for the get() to allow it.
Your second query doesn't work because it's attempting to get all of the documents in events collection. Your rule does not specifically allow that. It looks like you expect your rule to filter out the events that aren't allowed access based on the contents an unknown number of other documents. You need to be aware that security rules are not filters. Please click through to the documentation and read that section. The client must only request documents that are known to be readable according to rules. It can't depend on rules to filter documents that are not allowed.
If you want to be able to query for all events that the current user has access to, your current database structure will not work. You will need to put all the relevant information in the events collection itself. This means you should consider something like putting the UID of each user that's allowed to read the event in the document itself, then filter on that field. Or have some other collection that you can query in this way.

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).