• Stars
    star
    920
  • Rank 49,655 (Top 1.0 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 12 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Authorization package for Meteor, compatible with built-in accounts packages

meteor-roles v3

Authorization package for Meteor - compatible with built-in accounts package.

There are also older versions of this package:


Table of Contents


Contributors

Thanks to:


Authorization

This package lets you attach roles to a user which you can then check against later when deciding whether to grant access to Meteor methods or publish data. The core concept is very simple, essentially you are creating an assignment of roles to a user and then checking for the existence of those roles later. This package provides helper methods to make the process of adding, removing, and verifying those roles easier.


Permissions vs roles (or What's in a name...)

Although the name of this package is roles, you can define your roles, scopes or permissions however you like. They are essentially just tags that you assign to a user and which you can check upon later.

You can have traditional roles like, admin or webmaster, or you can assign more granular permissions such as, view-secrets, users.view, or users.manage. Often, more granular is actually better because you are able to handle all those pesky edge cases that come up in real-life usage without creating a ton of higher-level roles. With the roles package, it's all just a role object.

Roles can be put into a hierarchy. Roles can have multiple parents and can be children (subroles) of multiple roles. If a parent role is set to the user, all its descendants are also applying. You can use this to create "super roles" aggregating permissions all the way through the bottom of the tree. For example, you could name two top-level roles user and admin and then you could use your second-level roles as permissions and name them USERS_VIEW, POST_EDIT, and similar. Then you could set admin role as parent role for USERS_VIEW and POST_EDIT, while user would be parent only of the POST_EDIT role. You can then assign user and admin roles to your users. And if you need to change permissions later for the whole role, just add or remove children roles. You can create roles from this example with:

import { Roles } from 'meteor/alanning:roles';

Roles.createRole('user');
Roles.createRole('admin');
Roles.createRole('USERS_VIEW');
Roles.createRole('POST_EDIT');
Roles.addRolesToParent('USERS_VIEW', 'admin');
Roles.addRolesToParent('POST_EDIT', 'admin');
Roles.addRolesToParent('POST_EDIT', 'user');

What are "scopes"?

Sometimes it is useful to let a user have independent sets of roles. The roles package calls these independent sets "scopes" for lack of a better term. You can use them to represent various communities inside of your application. Or maybe your application supports multiple tenants. You can put each of those tenants into their own scope. Alternatively, you can use scopes to represent various resources you have.

Users can have both scope roles assigned, and global roles. Global roles are in effect for all scopes. But scopes are independent from each other. Users can have one set of roles in scope A and another set of roles in scope B. Let's go through an example of this using soccer/football teams as scopes.

Roles.addUsersToRoles(joesUserId, ['manage-team','schedule-game'], 'manchester-united.com');
Roles.addUsersToRoles(joesUserId, ['player','goalie'], 'real-madrid.com');

Roles.userIsInRole(joesUserId, 'manage-team', 'manchester-united.com'); // true
Roles.userIsInRole(joesUserId, 'manage-team', 'real-madrid.com'); // false

In this example we can see that Joe manages Manchester United and plays for Real Madrid. By using scopes, we can assign roles independently and make sure that they don't get mixed up between scopes.

Now, let's take a look at how to use the global roles. Say we want to give Joe permission to do something across all of our scopes. That is what the global roles are for:

Roles.addUsersToRoles(joesUserId, 'super-admin', null); // Or you could just omit the last argument.

if (Roles.userIsInRole(joesUserId, ['manage-team', 'super-admin'], 'real-madrid.com')) {
  // True! Even though Joe doesn't manage Real Madrid, he has
  // a 'super-admin' global role so this check succeeds.
}

Changes to default Meteor behavior

  1. A new collection Meteor.roleAssignment contains the information which role has been assigned to which user.
  2. A new collection Meteor.roles contains a global list of defined role names.
  3. All existing roles are automatically published to the client.

Installing

  1. Add one of the built-in accounts packages so the Meteor.users collection exists. From a command prompt:

    meteor add accounts-password
  2. Add this package to your project. From a command prompt:

    meteor add alanning:roles
  3. Publish the role assignments you need to the client:

    Meteor.publish(null, function () {
      if (this.userId) {
        return Meteor.roleAssignment.find({ 'user._id': this.userId });
      } else {
        this.ready()
      }
    })
  4. Run your application:

    meteor

Migration to 3.0

If you are currently using this package in a version older than 2.x, please upgrade to 2.0 by running the migration script required there: https://github.com/Meteor-Community-Packages/meteor-roles/tree/v2#migration-to-20

In meteor-roles 3.0, functions are mostly backwards compatible with 2.x, but roles are stored differently in the database. Please take a backup of the users collection before migrating. To migrate the database to the new schema, run Meteor._forwardMigrate2() on the server:

meteor shell
> Package['alanning:roles'].Roles._forwardMigrate2()

In case something fails, there is also a script available for rolling back the changes. But be warned that a backward migration takes a magnitude longer than a foward migration. To migrate the database back to the old schema, run Meteor._backwardMigrate2() on the server:

meteor shell
> Package['alanning:roles'].Roles._backwardMigrate2()

Changes between 2.x and 3.0

Here is the list of important changes between meteor-roles 2.x and 3.0 to consider when migrating to 3.0:

  • Role assignments have been moved from the users documents to a separate collection called role-assignment, available at Meteor.roleAssignment.
  • Role assignments are not published automatically. If you want all your role-assignments to be published automatically please include the following code:
Meteor.publish(null, function () {
  if (this.userId) {
    return Meteor.roleAssignment.find({ 'user._id': this.userId });
  } else {
    this.ready()
  }
})
  • [BC] The behavior of getRolesForUser() used with the option fullObjects changed. In case you need the old behavior ...
  • Added option anyScope to removeUsersFromRoles()
  • Add option onlyScoped to getRolesForUser() to allow limiting the result to only scoped permissions
  • All functions (excepted for those listed above) work with 2.x arguments, but in 3.x accept extra arguments and/or options.
  • Details and reasoning can be found in #276

Usage Examples


Here are some potential use cases:


-- Server --

Add users to roles:

var users = [
      {name:"Normal User",email:"[email protected]",roles:[]},
      {name:"View-Secrets User",email:"[email protected]",roles:['view-secrets']},
      {name:"Manage-Users User",email:"[email protected]",roles:['manage-users']},
      {name:"Admin User",email:"[email protected]",roles:['admin']}
    ];

users.forEach(function (user) {
  var id;

  id = Accounts.createUser({
    email: user.email,
    password: "apple1",
    profile: { name: user.name }
  });

  if (Meteor.roleAssignment.find({ 'user._id': id }).count() === 0) {
    import { Roles } from 'meteor/alanning:roles';
    
    user.roles.forEach(function (role) {
      Roles.createRole(role, {unlessExists: true});
    });
    // Need _id of existing user record so this call must come after `Accounts.createUser`.
    Roles.addUsersToRoles(id, user.roles);
  }

});

Note that the `Roles.addUsersToRoles` call needs to come _after_ `Accounts.createUser` or else the roles package won't be able to find the user record (since it hasn't been created yet). You can use `postSignUpHook` to assign roles when using [user accounts package](https://github.com/meteor-useraccounts/core). This SO answer gives more detail: http://stackoverflow.com/a/22650399/219238

Check user roles before publishing sensitive data:

// server/publish.js
import { Roles } from 'meteor/alanning:roles'

// Give authorized users access to sensitive data by scope
Meteor.publish('secrets', function (scope) {
  check(scope, String);

  if (Roles.userIsInRole(this.userId, ['view-secrets','admin'], scope)) {
    
    return Meteor.secrets.find({scope: scope});
    
  } else {

    // user not authorized. do not publish secrets
    this.stop();
    return;

  }
});

Prevent non-authorized users from creating new users:

Accounts.validateNewUser(function (user) {
  import { Roles } from 'meteor/alanning:roles'
  
  var loggedInUser = Meteor.user();

  if (Roles.userIsInRole(loggedInUser, ['admin','manage-users'])) {
    return true;
  }

  throw new Meteor.Error('unauthorized', "Not authorized to create new users");
});

Prevent access to certain functionality, such as deleting a user:

// server/userMethods.js
import { Roles } from 'meteor/alanning:roles'

Meteor.methods({
  /**
   * Revokes roles for a user in a specific scope.
   * 
   * @method revokeUser
   * @param {String} targetUserId ID of user to revoke roles for.
   * @param {String} scope Company to update roles for.
   */
  revokeUser: function (targetUserId, scope) {
    check(targetUserId, String);
    check(scope, String);
  
    var loggedInUser = Meteor.user();

    if (!loggedInUser ||
        !Roles.userIsInRole(loggedInUser, 
                            ['manage-users', 'support-staff'], scope)) {
      throw new Meteor.Error('access-denied', "Access denied")
    }

    // remove roles for target scope
    Roles.setUserRoles(targetUserId, [], scope)
  }
})

Manage a user's roles:

// server/userMethods.js
import { Roles } from 'meteor/alanning:roles'

Meteor.methods({
  /**
   * Update a user's roles.
   *
   * @param {Object} targetUserId Id of user to update.
   * @param {Array} roles User's new roles.
   * @param {String} scope Company to update roles for.
   */
  updateRoles: function (targetUserId, roles, scope) {
    check(targetUserId, String);
    check(roles, [String]);
    check(scope, String);

    var loggedInUser = Meteor.user();

    if (!loggedInUser ||
        !Roles.userIsInRole(loggedInUser, 
                            ['manage-users', 'support-staff'], scope)) {
      throw new Meteor.Error('access-denied', "Access denied");
    }

    Roles.setUserRoles(targetUserId, roles, scope);
  }
})

-- Client --

Client javascript does not by default have access to all the same Roles functions as the server unless you publish these role-assignments. In addition, Blaze will have the addition of a isInRole handlebars helper which is automatically registered by the Roles package.

As with all Meteor applications, client-side checks are a convenience, rather than a true security implementation since Meteor bundles the same client-side code to all users. Providing the Roles functions client-side also allows for latency compensation during Meteor method calls. Roles functions which modify the database should not be called directly, but inside the Meteor methods.

NOTE: Any sensitive data needs to be controlled server-side to prevent unwanted disclosure. To be clear, Meteor sends all templates, client-side javascript, and published data to the client's browser. This is by design and is a good thing. The following example is just sugar to help improve the user experience for normal users. Those interested in seeing the 'admin_nav' template in the example below will still be able to do so by manually reading the bundled client.js file. It won't be pretty but it is possible. But this is not a problem as long as the actual data is restricted server-side.

To check for global roles or when not using scopes:

<!-- client/myApp.html -->

<template name="header">
  ... regular header stuff
  {{#if isInRole 'admin'}}
    {{> admin_nav}}  
  {{/if}}
  {{#if isInRole 'admin,editor'}}
    {{> editor_stuff}}
  {{/if}}
</template>

To check for roles when using scopes:

<!-- client/myApp.html -->

<template name="header">
  ... regular header stuff
  {{#if isInRole 'admin,editor' 'group1'}}
    {{> editor_stuff}}  
  {{/if}}
</template>

API Docs

Online API docs found here: https://meteor-community-packages.github.io/meteor-roles/

API docs generated using YUIDoc

To re-generate documentation:

  1. install YUIDoc
  2. cd meteor-roles
  3. yuidoc

To serve documentation locally:

  1. install YUIDoc
  2. cd meteor-roles
  3. yuidoc --server 5000
  4. point browser at http://localhost:5000/

Example Apps

The examples directory contains Meteor apps which show off the following features:

  • Server-side publishing with authorization to secure sensitive data

  • Client-side navigation with link visibility based on user roles

  • 'Sign-in required' app with up-front login page using accounts-ui

  • Client-side routing

    1. git clone https://github.com/Meteor-Community-Packages/meteor-roles.git
    2. choose an example, eg.
    • cd meteor-roles/examples/iron-router or
    • cd meteor-roles/examples/flow-router
    1. meteor
    2. point browser to http://localhost:3000

Tests

To run tests:

  1. cd meteor-roles
  2. npm run test

More Repositories

1

meteor-autoform

AutoForm is a Meteor package that adds UI components and helpers to easily create basic forms with automatic insert and update events, and automatic reactive validation.
JavaScript
1,439
star
2

Meteor-CollectionFS

Reactive file manager for Meteor
JavaScript
1,051
star
3

meteor-collection2

A Meteor package that extends Mongo.Collection to provide support for specifying a schema and then validating against that schema when inserting and updating.
JavaScript
1,024
star
4

meteor-simple-schema

Meteor integration package for simpl-schema
JavaScript
920
star
5

meteor-collection-hooks

Meteor Collection Hooks
JavaScript
657
star
6

ground-db

GroundDB is a thin layer providing Meteor offline database and methods
JavaScript
569
star
7

meteor-user-status

Track user connection state and inactivity in Meteor.
JavaScript
557
star
8

meteor-publish-composite

Meteor.publishComposite provides a flexible way to publish a set of related documents from various collections using a reactive join
JavaScript
553
star
9

raix-push

DEPRECATED: Push notifications for cordova (ios, android) browser (Chrome, Safari, Firefox)
JavaScript
515
star
10

meteor-tabular

Reactive datatables for large or small datasets
JavaScript
363
star
11

meteor-autocomplete

Client/server autocompletion designed for Meteor's collections and reactivity.
CoffeeScript
350
star
12

meteor-scss

Node-sass wrapped to work with meteor.
JavaScript
311
star
13

meteor-partitioner

Transparently divide a single meteor app into several different instances shared between different groups of users.
CoffeeScript
152
star
14

meteor-timesync

NTP-style time synchronization between server and client, and facilities to use server time reactively in Meteor applications.
JavaScript
118
star
15

meteor-link-accounts

Meteor link account package. based on this PR https://github.com/meteor/meteor/pull/1133
JavaScript
116
star
16

mongo-collection-instances

πŸ—‚ Meteor package allowing Mongo Collection instance lookup by collection name
JavaScript
73
star
17

Meteor-EventEmitter

Client and server event emitter
JavaScript
72
star
18

meteor-postcss

PostCSS for Meteor
JavaScript
68
star
19

meteor-mocha

A Mocha test driver package for Meteor 1.3+. This package reports server AND client test results in the server console and can be used for running tests on a CI server or locally.
JavaScript
67
star
20

meteor-elastic-apm

Meteor Elastic APM integration
JavaScript
57
star
21

organization

Discussions on organization of the organization 🎩
41
star
22

meteor-schema-index

Control some MongoDB indexing with schema options
JavaScript
38
star
23

meteor-publication-collector

Test a Meteor publication by collecting its output.
JavaScript
33
star
24

meteor-collection-extensions

Safely and easily extend the (Meteor/Mongo).Collection constructor with custom functionality.
JavaScript
31
star
25

stratosphere

Meteor private package server
JavaScript
28
star
26

meteor-method-hooks

JavaScript
26
star
27

meteor-autoform-select2

Custom select2 input type for AutoForm
JavaScript
25
star
28

meteor-autoform-bs-datepicker

Custom "bootstrap-datepicker" input type for AutoForm
JavaScript
25
star
29

react-router-ssr

Simple isomorphic React SSR for Meteor with subscribed data re-hydration
JavaScript
24
star
30

denormalize

Simple denormalization for Meteor
JavaScript
20
star
31

meteor-autoform-bs-datetimepicker

Custom bootstrap-datetimepicker input type with timezone support for AutoForm
JavaScript
17
star
32

meteor-packages

Client for Meteor Package Server API
JavaScript
14
star
33

Packosphere

A Meteor package explorer for you, and if you are so inclined to help build it, by you.
TypeScript
13
star
34

meteor-schema-deny

Deny inserting or updating certain properties through schema options
JavaScript
12
star
35

check-npm-versions

Enforces "peer" npm dependencies in Meteor 1.3+ Atmosphere packages.
TypeScript
11
star
36

meteor-browser-tests

A helper package for Meteor test driver packages. Runs client tests in a headless browser.
JavaScript
11
star
37

template-package

Template package with CI and everything else to get started quickly with creating a new FOSS Meteor package.
JavaScript
10
star
38

meteor-stylus

A fork of meteor:stylus with mquandalle:stylus plugins
JavaScript
8
star
39

meteor-minifiers-autoprefix

JavaScript
6
star
40

meteor-autoform-bs-button-group-input

A Bootstrap button group theme for the "select-checkbox" and "select-radio" AutoForm input types
JavaScript
6
star
41

ground-minimax

Minimax is a thin layer for Meteor providing EJSON.minify and EJSON.maxify
JavaScript
6
star
42

meteor-typescript

Typescript compiler package
TypeScript
5
star
43

meteor-api-untethered

A collection of packages to make Meteor available to other environments.
JavaScript
5
star
44

awesome-meteor

Curated list of awesome Meteor.js things.
5
star
45

Meteor-EventState

DEPRECATED: Evented state
JavaScript
4
star
46

meteor-autoform-themes

Officially supported themes for aldeed:autoform
JavaScript
3
star
47

push

JavaScript
2
star