Skip to content

Philosophy

express-mongoose-acl exposes REST API endpoints corresponding to mongoose data models in Express routes. It builds the backend database security layer by decorating mongoose queries, which enables dynamic frontend mongoose-like query options.

NPM Version Package License NPM Downloads

Motivation

REST (Representational State Transfer) is an API protocol which was introduced in a 2000, and is one of major API standards today. Besides its many benefits, there are also disadvantages to RESTful APIs. One of the disadvantages is that there is no good solution to manage object data at a fine-grained level unless opening up more API endpoints.

Let's say we have a User model contains following fields:

- name
- address
- roles
- creditBalance
- loginDate
  • GET /users/{id} to retrieve an user entity identified by user ID

By making a request to this endpoint, it will return the user's data including the 6 fields above. What if we want to allow selected fields depending on the requester's roles on the system? admin and member. Probably, we will include conditional logic at the backend to exclude fields that non-admin is not allowed to read. But, does it actually solve the cases we will have when building a web application? Even though admin is permitted to read all fields of the entity, some fields could be redundant for certain screens.

To retrieve selected fields only, we might send more information to the API call, such as GET /users/{id}?include=partial[|all]. However, this approach will make the backend codebase messy soon as more screens require diffrent entity fields and more backend conditional logic to handle all possible cases.

  • PUT /users/{id} to update an user entity identified by user ID

Typically, UPDATE endpoint contains more complicated logic to prevent unwanted updates by wrong user types. For example, there might be a case that admin can update the first 5 fields while the user itself can update the first 3 fields only. If handling each scenario at the backend codebase, it will be inevitable to have multiple conditional logic depends on the complexity of the user types and the entity relationships.

Concept

The idea is to build a security boundary defined in a schema for each resource to be consumed at the backend routes; because this security layer works as a wrapper around the request information sent by the browser, it gives the frontend codebase the most flexibility to build queries and manage data within the given API endpoints by the library.

Object permissions are checked if global permissions pass and define whether a user has the ability to perform a specific action on a single object. These are also known as row level permissions.

  • Global Permissions

Global permissions are system-wide and granted to authenticated users based on their roles and allow role-based access control (RBAC) to the backend system. Global permissions are expected in Express request object (e.g. req._permissions) and used to apply access control to the system and resources.

  • Document Permissions

Document permissions are object-level privileges and defined to allow performing specific actions on a single Mongoose document.

  • Role-based Security

  • Document-level Security

  • Field-level security

  • Base Query

Base queries are generated to decoreate Mongoose Query object to apply global permissions to a target collection.

  • Mongoose Query Syntax

Library API endpoints take a similar request structure as Mongoose Syntax, e.g. query, select, and populate, to reduce the learning curve of a new tool.