Mongoose adapter for Cerbos Query Plans

Published by Alex Olivier on August 29, 2023
Mongoose adapter for Cerbos Query Plans

The Cerbos Query Plan capability addresses a complex challenge in decoupled authorization - specifically, determining the list of resources for which a user possesses a particular permission. Instead of the inefficient approach of fetching all instances of a resource type and then individually checking permissions (which would seriously impact performance), Cerbos offers a query plan. This plan provides your application with a structured set of conditions (AST) that can be applied to your data retrieval logic. The resulting conditions are derived from the policies and are optimized to retrieve only those instances from the data store that a user is permitted to access.

However, translating this set of conditions (AST) into a relevant WHERE clause or filter can be difficult. To simplify this process, we have developed adapters for popular Object-Relational Mapping (ORM) frameworks. These adapters handle the conversion for you. We are now pleased to introduce an adapter for Mongoose to our collection.

By using this adapter, implementing result filtering based on policies becomes straightforward. You simply need to pass the plan to the adapter and then include the resulting conditions in the find method of your models.

import { GRPC as Cerbos } from "@cerbos/grpc";
import mongoose from "mongoose";

import { queryPlanToMongoose, PlanKind } from "@cerbos/orm-mongoose";

// connect to mongo
await mongoose.connect("mongodb://127.0.0.1:27017/test");
// connect to Cerbos PDP
const cerbos = new Cerbos("localhost:3592", { tls: false });

// Mongoose models (schema excluded for brevity)
const MyModel = mongoose.model("MyModel", ....);

// Fetch the query plan from Cerbos passing in the principal
// resource type and action
const queryPlan = await cerbos.planResources({
  principal: {....},
  resource: { kind: "resourceKind" },
  action: "view"
})

// Generate the mongoose filter from the query plan
const result = queryPlanToMongoose({
  queryPlan,
  fieldNameMapper: {
    "request.resource.attr.aFieldName": "mongooseModelFieldName"
  }
});

// The query plan says the user would always be denied
// return empty or throw an error depending on your app.
if(result.kind == PlanKind.ALWAYS_DENIED) {
  return console.log([]);
}

// Pass the filters in as where conditions
// If you have prexisting where conditions, you can pass them in an $and clause
const result = await MyModel.find({
  ...result.filters
});

console.log(result)

You can find more details on the query plan repo - which also contains adapters for Prisma and SQLAlchemy - as well as a fully functioning application using Mongoose as its ORM.

Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team