Coder Social home page Coder Social logo

dmv's Introduction

Build status Code Climate Dependency Status

DMV

Intro

DMV is a MEAN stack library that lets you define roles and permissions. It lets you express thoughts like:

student can read chapter. teacher can assign chapter.

I like to think about the previous sentence like this

  [Role] can [Permission]
                  ^
                /   \
            [Verb] [Noun]

Practical Example

We use DMV to register the roles and nouns:

dmv.role('author');
dmv.role('moderator');
dmv.role('admin');

dmv.noun('article', function(articleNoun) {
  // by default the noun has crud verbs
  // we're adding the approve verb here
  articleNoun.verb('approve');

  // we say that admins can do all verbs
  articleNoun.authorize('admin', '*');

  // moderators can 
  articleNoun.authorize('moderator', ['approve', 'read']);

  // authors can
  articleNoun.authorize('author', ['create', 'update'])

});

dmv.noun('comment', function(commentNoun) {
  commentNoun.verb('approve');

  // we say that admins can do all verbs
  commentNoun.authorize('admin', '*');

  // moderators can 
  commentNoun.authorize('moderator', ['approve']);
});

Mongoose

const userSchema = new mongoose.Schema({
  name: String
  // ...
});

//

/**
 * This plugin will add three paths to your user model
 * 
 * - roles
 * - permissionWhitelist
 * - permissionBlacklist
 * 
 * More on the latter two in a minute.
 * It will also add the `can` method.
 */
userSchema.plugin(dmv.mongoosePlugin);

mongoose.model('User', userSchema);

You can use your user instance's can method like this

const sarah = new User({
  roles: ['author'],
  name: 'Sarah'
});

const alex = new User({
  roles: ['moderator'],
  name: 'Alex'
});

const hilary = new User({
  roles: ['admin'],
  name: 'Hilary'
});

sarah.can('create', 'article'); // => true
sarah.can('update', 'article'); // => true
sarah.can('delete', 'article'); // => false

alex.can('approve', 'article'); // true
alex.can('create', 'article');  // false

hilary.can('delete', 'article') // true
hilary.can('approve', 'article') // true

Express

const dmv = require('dmv');
const auth = dmv.expressMiddleware

// articles router

// everyone can read all articles
router.get('/', controller.index);
router.post('/', auth.permits('create', 'article'), controller.create);

router.use('/:id', controller.load);

router.get('/:id', controller.read);


router.put('/:id', auth.permits('update', 'article'), controller.update);
router.delete('/:id', auth.permits('delete', 'article'), controller.delete);

Dmv's express middleware calls the can method on the req.user by default. If your application doesn't have a req.user, you can define another function to return a user object.

auth.user(function(req, res) {
  return req.yourOtherUserObject;
});

If you have a router that really only concerns itself with one noun (like above), you can use permitsFactory to generate a version of permits that only works for one noun. That would look like this.

const dmv = require('dmv');
const auth = dmv.expressMiddleware
const permits = auth.permitsFactory('article')

// articles router

// everyone can read all articles
router.get('/', controller.index);
router.post('/', permits('create'), controller.create);

// ...

Angular

Note: we assume you use ui-router for now.

First, you can use our browser file

<script src="./path/to/browser.js"></script>

We expose the dmv object on the global scope. Dmv is isomorphic so you can run the same code defining permissions and roles that you ran on the server. Doing this will depend on your build process and environment.

Once your frontend knows about the roles and permissions you can plug in our angular module

angular.module('yourApp', ['ui.router', 'dmv']);

Then, you need to hook us into your user model. We expose a factory called canPlugin. canPlugin is a function that gives your user model a can method by modifying its prototype. You pass the prototype to canPlugin. You do it like this.

yourModule.factory('YourUserClass', funciton($httpMaybe?, canPlugin) {
  const User = function() {
    // your constructor
  };

  canPlugin(User.prototype);

  return User;
})

You can call can on any of your user instances. In addition, we've added a hasRole method on your user class.

You need to tell dmv how to find your logged in user. We've exposed a method called getUser on our authConfig factory. You can inject your own angular services. You just need to return your user. The getUser method also attaches can and hasRole to your $rootScope so you can show and hide view segments easily.

yourModule
  .run(function(authConfig) {
    // Note, ng-annotate and others won't annotate the dependencies for this method. So if you minify in your build process, you should annotate your own dependencies.
    authConfig.getUser(function(YourUserService, YourAuthService) {
      return YouAuthService.getLoggedInUser();
    })
  })
  

Then you can add a special auth property to your ui-router state definitions.

yourModule
  .config(function($stateProvider) {
    $stateProvider
      .state('articles', {
        url: '/articles',
        template: 'everyone can see these great articles',
        auth: false // you could omit this, it just documents here
      })
      .state('new_article', {
        url: '/newarticle',
        template: 'form only for those who can write',
        auth: {
          create: 'article'
        }
      });

  }) 

The auth property can take a boolean, function or an object. If you pass true to auth it will ensure the user has logged in. An object lets you specify the nouns and verbs that the user must have access to to load the route. You can also pass auth a function. If you return true from that function, the user will be allowed to enter the state. The function's this is bound to the state change event. It is passed the user object and the next state.

Our module will broadcast a NOT_AUTHORIZED event on the root scope if a user goes to a route they are not authorized to see. We will broadcast a NOT_AUTHENTICATED event if a user that hasn't logged in attempts to see a route that has a truthy auth property.

With these events you can configure the behavior of failed route loads.

dmv's People

Contributors

andrewmash avatar gtelljohann avatar rpastor90 avatar zekenie avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.