New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

bookshelf-shield

Package Overview
Dependencies
Maintainers
6
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bookshelf-shield

Access control list based authorization for bookshelf models

  • 2.2.2
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
0
decreased by-100%
Maintainers
6
Weekly downloads
 
Created
Source

Shield

bookshelf-shield

Form a protective shield around your bookshelf models. This module adds ACL-based authorization, and a CRUD API to bookshelf models.

Codeship Status for MRN-Code/bookshelf-shield

Dependencies

relations

As of right now, bookshelf-shield only can interact with the ACL module called relations Provides an intuitive interface for storing and querying Access Conrtol Lists. Relations is used to determine whether a user has been granted access to perform an action on the model.

ES6

This module utilizes ES6 features, including classes, arrow functions and Promises. As a result node 4.0.0+ is required.

Usage

  1. Set up your ACL
relations.define('study', {
    PI: ['read_Study'],
    siteAdmin: [
        'read_Study',
        'update_Study',
        'create_Study',
        'delete_Study'
    ]
});
  1. Set up you bookshelf models
const models = {
    Study: bookshelf.Model.extend({tableName: 'mrs_studies'}),
    //...
};
  1. Create a shieldConfig for each model (see configuration section for more)
const config = [
    {
        defaults: {
            modelName: 'Study',
            authKey: 'id',
            aclContextName: 'study'
        },
        //optional action-specific configs here
    },
    //...
  1. Shields up!
const shield = require('bookshelf-shield');
shield({
    config: config,
    acl: relations,
    models: models
});

API

Once a model has been shielded, you can interact with it using a standard CRUD API, rather than the traditional fetch, save, destroy bookshelf API. This was implemented to more easily map to user's permissions.

  1. create
    const user = { username: 'dylan' };
    const widget = new Widget({ color: 'blue' });
    widget.create(user).then((newWidget) => {
        //new Widget successfully created
    }).catch((error) => {
        //handle Error
    });
  1. read
    const user = { username: 'dylan' };
    const widget = new Widget({ id: '101' });
    widget.read(user).then((newWidget) => {
        //newWidget successfully read
    }).catch((error) => {
        //handle Error
    });
  1. readAll
    const user = { username: 'dylan' };
    const widget = new Widget();
    widget.query({color: 'blue'});
    widget.readAll(user).then((newWidgets) => {
        //widgets successfully read into newWidgets collection
    }).catch((error) => {
        //handle Error
    });
  1. update (note: by default, read access is required to perform an update)
    const user = { username: 'dylan' };
    widget.set('color', 'red');
    widget.update(user).then((newWidget) => {
        //widget successfully updated
    }).catch((error) => {
        //handle Error
    });
  1. delete (note: by default, read access is required to perform a delete)
    const user = { username: 'dylan' };
    const widget = new Widget({ id: '101' });
    widget.delete(user).then((newWidget) => {
        //widgets successfully deleted (newWidget should now be empty)
    }).catch((error) => {
        //handle Error
    });
  1. bypass
    const widget = new Widget({ id: '101' });
    widget.bypass('fetch').then((newWidget) => {
        //new Widget successfully created
    }).catch((error) => {
        //handle Error
    });

Configuration

Each model to be shielded requires a config object. During initialization, these config objects should be provided as an array. Here is an example config object:

module.exports = {
    defaults: { // These defaults will be applied to all CRUD access methods, unless overridden below.
        modelName: 'Study', // The name of the model: must match the key associated with the model in the models object passed to shield init.
        authKey: 'id', // The property that should be used for authorization.
        aclContextName: 'study' // The name of the ACL (relations) context to be used
    },
    create: { //specifying any CRUD method allows you to override the defaults secified above
        authKey: 'siteId', //alternative auth key to be used when evaluating create access
        aclContextName: 'site',
        method: function validateSiteAdmin(user) {
            // this is a cusom authentication method that will be invoked instead of the generic method.
            // `this` refers to the current instance of the bookshelf model
            const siteId = this.get('siteId');
            // data stored on the shield can be accessed through the current object's constructor (the bookshelf Model).
            const Model = this.constructor;
            const aclContext = Model.shield.acl.site;
            const aclQuestion = 'can ' +
                user.username +
                ' create_Study from ' +
                siteId;

            if (!siteId) {
                return Promise.reject(
                    new Error('Study has no valide siteId')
                );
            }

            return aclContext(aclQuestion).then(function checkAuth(allow) {
                let errorMsg;
                if (allow) {
                    return allow;
                }

                errorMsg =
                    user.username +
                    ' cannot create studies in Site ' +
                    siteId;
                throw new Error(errorMsg);
            });
        }
    }
};

Because there are no configuration objects specified for read, update and delete operations, those operations will be protected using the generic method (see below).

Generic Auth Method

Unless a custom method is specified in the Model's config, the following generic method will be applied:

// Note options is the config for the current Model and action
function genericAuthMethod(user) {
    const authVal = this.get(options.authKey);
    const aclQuestion = 'can ' +
        user.username +
        ' ' +
        permissionName +
        ' from ' +
        authVal;
    const aclContext = options.acl[aclContextName];

    //TODO: optimize to cache perms instead of loading from redis
    return aclContext(aclQuestion).then(function checkAuth(allow) {
        let errorMsg;
        if (allow) {
            return allow;
        }

        errorMsg = [
            user.username,
            'cannot',
            permissionName.replace('_', ' '),
            'in',
            options.authKey,
            '`' + authVal + '`'
        ].join(' ');
        throw new AuthError(errorMsg);
    });

Examples

See test/integration/main.js for a full example

Tests

Fully unit and integration tested

Contributing

Please follow the MRN Javascript Style Guide (forked from AirBnB). Use grunt lint to check yo-self

FAQs

Package last updated on 16 Aug 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc