Accept or Reject Queries

Your security rules can also accept or reject queries based on their constraints. The request.query variable contains the limit , offset , and orderBy properties of a query. For example, your security rules can deny any query that doesn't limit the maximum number of documents retrieved to a certain range:

allow list: if request.query.limit <= 10;

Nesting match statements

Data in Cloud Firestore is organized into collections of documents, and each document may extend the hierarchy through subcollections.

When nesting match statements, the path of the inner match statement is always relative to the path of the outer match statement. The following rulesets are therefore equivalent:

service cloud.firestore {

match /databases/{database}/documents {

match /users/{userId} {

match /posts/{postId} {

allow read, write: if <condition>;

}

} // equivalent to:

match /users/{userId}/posts/{postId} {

allow read, write: if <condition>;

} }

}

Recursive wildcards

If you want rules to apply to an arbitrarily deep hierarchy, use the recursive wildcard syntax, {name=**} . For example:

service cloud.firestore {

match /databases/{database}/documents {

match /users/{document=**} {

allow read, write: if <condition>;

}

}

}

Security on Parse

Parse takes a slightly different approach. When using Parse, you can specify what operations are allowed per class. This lets you restrict the ways in which clients can access or modify your classes. The most common use-case is to limit client from creating new classes on Parse.

Once restricted, classes may only be created from the Data Browser or with a masterKey.

Parse has a lot to offer, but I’m going to focus on 3 Parse classes:

ParseACL

ParseUser

ParseRole

ParseACL — Access Control Lists

A ParseACL is used to control which users and roles can access or modify a particular object. You can grant read and write permissions separately to specific users or groups of users.

Security For ParseUser Objects

The ParseUser class is secured by default. Data stored in a ParseUser can only be modified by that user. By default, the data can still be read by any client.

Example from Back4App: userId = 1234, Public Read permission, Write permission is set to userId only

Back4App is a backend-as-a-service platform powered by Parse Open Source which you can use to build your app faster, host it and keep full control over your Backend.

Security For Other Objects

We can use a Parse.ACL to limit the read and write access to a specific user:

const Test = Parse.Object.extend("Test");

const privateTest = new Test();

privateTest.set("description", "This test is private!");

privateTest.setACL(new Parse.ACL(Parse.User.current()));

privateTest.save();

Permissions can also be granted to a group of users:

const Test = Parse.Object.extend("Test");

const groupTest = new Test();

const groupACL = new Parse.ACL(); for (let i = 0; i < users.length; i++) {

groupACL.setReadAccess(users[i], true);

groupACL.setWriteAccess(users[i], true);

} groupTest.setACL(groupACL);

groupTest.save();

All of this is great, but what if you’re trying to implement something not so trivial?

Please welcome Roles!

Parse supports a form of Role-based Access Control, which is more sophisticated than simple ACL.

Roles provide a logical way of grouping users with common access privileges to your Parse data. Roles are named objects that contain users and other roles. Any permission granted to a role is implicitly granted to its users.

This feature allows you to limit the access without having to manually grant permission to every resource for each user.

Security for Role Objects

To create a new Parse.Role , you would write:

const roleACL = new Parse.ACL();

roleACL.setPublicReadAccess(true);

const role = new Parse.Role("Admin", roleACL);

role.save(); // By specifying no write privileges for the ACL, we can ensure the role cannot be altered.

You can add users and roles that should inherit your new role’s permissions through the “users” and “roles” relations on Parse.Role :

const role = new Parse.Role("Admin", roleACL);

role.getUsers().add(usersToAddToRole);

role.getRoles().add(rolesToAddToRole);

role.save();

Conclusion and a short summary

I tried to summarize my comparison in a table:

Parse provides a wider range of options, by allowing the relationship between objects, users, and their permissions using ACL and roles.

It is also good to know that Parse has more advanced features like using sessionToken in order to check the fulfillment of the ParseObject ’s ACL, or to use a masterKey which can override any ACLs, and it’ll bypass all the security mechanisms.

In addition, Parse allows you to split the cloud code into separate files and elegantly manage your permissions. There is a graphical display for each object, making it very easy for debugging.

Firebase provides the security rules for managing the permissions by direct access to documents. The fact that the permissions can be configured in a way that is nested allows for very readable code writing. The simulator for the UI and the emulator as npm package significantly increases the ability to test the rules and thus has a huge gap on Parse.

My personal recommendation is to thoroughly understand how these security rules work before your app grows to a significant user base. It is challenging to write these rules while your users are already using your app. I ran into several cases where developers were forced to change the structure of the database due to difficulties in writing security rules.

I hope that this article will help you choose the best service for your specific needs.