Skip to content

Files

Latest commit

a8cf4f9 · Jul 29, 2019

History

History
This branch is 1 commit ahead of, 1058 commits behind stalniy/casl:master.

casl-mongoose

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Dec 28, 2018
Dec 28, 2018
Mar 21, 2018
Sep 3, 2018
Feb 10, 2019
Mar 21, 2018
Feb 10, 2019
Nov 7, 2018
Jul 29, 2019
Feb 10, 2019

This package connects CASL and MongoDB. In other words, it allows to fetch records based on CASL rules from MongoDB. That means you can easily answer on the question: "Which records can be read?" or "Which records can be updated?". Lets see how

Installation

npm install @casl/mongoose @casl/ability

Getting Started

1. Integrating with mongoose

There are 2 plugins which allow to seamlessly integrate CASL into mongoose:

Accessible Records plugin

accessibleRecordsPlugin is a mongoose plugin which adds accessibleBy method to query and static methods. For example, you can add this plugin globally to all models

const { accessibleRecordsPlugin } = require('@casl/mongoose')
const mongoose = require('mongoose')

mongoose.plugin(accessibleRecordsPlugin)

Warning: make sure that you add that plugin before calling mongoose.model(...) method. Models which were defined before adding plugin will not include accessibleBy method.

Alternatively, you can selectively add plugin to any model:

// post.model.js
const mongoose = require('mongoose')
const { accessibleRecordsPlugin } = require('@casl/mongoose')

const Post = new mongoose.Schema({
  title: String,
  author: String
})

Post.plugin(accessibleRecordsPlugin)

module.exports = mongoose.model('Post', Post)

Afterwards you can fetch accessible records by doing this:

const Post = require('./post.model')
const ability = require('./ability') // defines Ability instance

Post.accessibleBy(ability).exec()

Check @casl/ability package to understand how to define abilities.

Permitted Fields plugin

permittedFieldsPlugin is a mongoose plugin which adds permittedFieldsBy method to instance and static methods. That method allow to retrieve accessible fields by ability:

const { permittedFieldsPlugin } = require('@casl/mongoose')
const mongoose = require('mongoose')
const PostSchema = require('./schema')
const ability = require('../ability') // defines Ability instance

PostSchema.plugin(permittedFieldsPlugin)
const Post = mongoose.model('Post', PostSchema)

const readableFields = Post.permittedFieldsBy(ability) // by default, returns fields for `read` action

Later, you can use that array of fields to return user only fields which he can read or pick ones from body which he can update!

const pick = require('lodash.pick')

app // express instance for example
  .patch('/posts/:id', (req, res) => {
    const updatableFields = Post.permittedFieldsBy(ability, 'update')
    const body = pick(req.body, updatableFields)

    // now `body` is an object of fields which user is allowed to update
  })

The same method exists on Model instance and takes into consideration rule conditions & object properties as well. For example, if you have the next rules:

const ability = AbilityBuilder.define(can => {
  can('read', 'Post', ['title'], { private: true })
  can('read', 'Post', ['title', 'description'], { private: false })
})
const post = new Post({ private: true, title: 'Private post' })

Post.permittedFieldsBy(ability) // ['title', 'description']
post.permittedFieldsBy(ability) // ['title']

Without knowing context (i.e., Post instance attributes) permittedFieldsBy can't return the correct permitted fields. That's why it's recommended to use instance method instead of class method!

2. Integrating with any MongoDB library

In case you don't use mongoose, this package provides toMongoQuery function which can convert CASL rules into MongoDB query. Lets see an example of how to fetch accessible records using raw MongoDB adapter

const { toMongoQuery } = require('@casl/mongoose')
const { MongoClient } = require('mongodb')
const ability = require('./ability') // allows to update posts if author equals "me"

MongoClient.connect('mongodb://localhost:27017/blog', function(err, db) {
  if (err) {
    return console.error(err)
  }

  const query = toMongoQuery(ability, 'Post', 'update') // e.g., { $or: [{ author: 'me' }] }

  if (query === null) {
    // user is not allowed to update any posts
  } else {
    db.collection('posts').find(query) // find all Posts where author equals 'me'
  }

  db.close();
})

See Database integration for details

Want to help?

Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing /d

License

MIT License