Skip to content
Branch: master
Find file History
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
spec feat(mongoose): wraps resulting query into additional `$and` Dec 28, 2018
src feat(mongoose): wraps resulting query into additional `$and` Dec 28, 2018
.npmignore
.npmrc Revert "feat(react:can): updates typescript declarations" Sep 3, 2018
CHANGELOG.md chore(release): @casl/mongoose@2.3.1 [skip ci] Feb 10, 2019
LICENSE docs(packages): updates license year Mar 21, 2018
README.md
index.d.ts fix(mongoose): adds optional options as null type (#127) Nov 7, 2018
package-lock.json
package.json chore(mongoose): change jest env to `node` Feb 10, 2019

README.md

CASL Mongoose @casl/mongoose NPM version CASL Documentation CASL Join the chat at https://gitter.im/stalniy-casl/casl

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

You can’t perform that action at this time.