Meteor Roles

  • https://atmospherejs.com/alanning/roles
  • https://github.com/alanning/meteor-roles

  • https://github.com/nicolaslopezj/roles

  • Utilisé par Orion

  • https://www.discovermeteor.com/blog/allow-deny-a-security-primer/

  • https://www.discovermeteor.com/blog/allow-deny-challenge-results/

Références

  • https://atmospherejs.com/ongoworks/security
  • https://github.com/ongoworks/meteor-security
  • https://atmospherejs.com/ryw/blog

TODO

Filtrer les publish selon roles et appartenance au group - Role: ? - Récupérer group d'un user et utiliser find({'$in': []})

Allow - insert - Role (pas de group) - update - Le group du user doit être dans le champs owner_group - remove

Tips - SimpleSchema - Ajout auto d'un champs owner

Schemas.Posts = new SimpleSchema({ title: { type: String, max: 60 }, owner: { type: String, regEx: SimpleSchema.RegEx.Id, autoValue: function() { if (this.isInsert) { return Meteor.userId(); } }, autoform: { options: function() { return _.map(Meteor.users.find().fetch(), function(user) { return { label: user.emails[0].address, value: user._id }; }); } } } });

Références - Utilisé par

  • https://atmospherejs.com/yogiben/admin
  • https://atmospherejs.com/reactioncommerce/core

Les différentes solutions

https://github.com/perak/user-roles
https://github.com/nicolaslopezj/roles
    - Utiliser par cms orion
https://github.com/possibilities/meteor-groups

Ajout au modèle utilisateur

Groupe Global { "_id" : "jdJpzpvCvqr9ZrXvm", "username" : "admin", ... "roles" : { "global_roles" : [ "admin" ] } }

Groupe client1 "roles" : { "group1" : [ "client" ] }

Installation


$ meteor add accounts-password
$ meteor add alanning:roles

La collection n'est pas publié par défaut, il faut le faire avec:

// server/publish.js
Meteor.publish(null, function (){
  return Meteor.roles.find({})
});

Créer / Supprimer des roles - hors user

Roles.createRole(role);

Roles.deleteRole(role);

// Refuse la suppression, si le role est affecté à des utilisateurs:

var foundExistingUser = Meteor.users.findOne({roles: {$in: [role]}},{fields: {_id: 1}})

Liste des roles d'un utilisateur

Renvoi un tableau de String non trié des roles du user

Roles.getRolesForUser(user_or_userid, optional_group);

Liste des utilisateurs pour un role

Retour un curseur des Meteor.users pour ce role

Roles.getUsersInRole(role, group);

Roles.getUsersInRole(role, group).fetch();

Roles.getUsersInRole("client").fetch()

Roles.getUsersInRole(["client", "manager"]).fetch()

Liste des groupes d'un utilisateur

Renvoi un tableau des groupes non trié

Ne doit fonctionner que si le groupe à été affecté en même temps que le role.

Roles.getGroupsForUser(user_or_userid, optional_role);

Controlleur personnalisé avec vérification des droits par route

Vérifie avant (onBeforeAction) si l'utilisateur à la permission pour cette route

ShopAdminController = this.ShopController.extend({
  onBeforeAction: function() {
    if (!ReactionCore.hasPermission(this.route.getName())) {
      this.render('unauthorized', {
        to: 'main'
      });
    } else {
      this.next();
    }
  }
});

Assigner des rôles au démarrage du server

https://github.com/alanning/meteor-roles/blob/master/examples/rolesWithAccountsUI/server/startup.js https://github.com/alanning/meteor-roles/blob/master/examples/iron-router/server/server.js

Supprimer les roles d'un utilisateur

A faire avant la suppression d'un utilisateur

Roles.setUserRoles(targetUserId, [], group)

Utilisation dans les Allow - Deny

BlacklistIp.allow({
  insert: function (userId, doc) {
    //error.invalidKeys` or by calling Books.simpleSchema().namedContext().invalidKeys()
    //error.reason
    return !! userId;
    //return true;
  },

  update: function (userId, doc, fieldNames, modifier) {
    return true;
  },

  remove: function (userId, doc) {
    return true;
  }
});

Assigner automatiquement un role par default

Intercepte la création d'un utilisateur par la méthode Accounts.createUser()

// server/methods.js Accounts.onCreateUser(function (options, user) { Roles.setRolesOnUserObj(user, ['admin','view-secrets']); return user });

Tips - Limiter la création d'utilisateurs

Autoriser seulement la création pour les roles admin ou manage-users:


//common
Accounts.validateNewUser(function (user) {
    var loggedInUser = Meteor.user();

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

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

Tips - Exemple de filtrage des données coté server - Par Groupe utilisateur


// server/publish.js
Meteor.publish('secrets', function (group) {
  if (Roles.userIsInRole(this.userId, ['view-secrets','admin'], group)) {

    return Meteor.secrets.find({group: group});

  } else {

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

  }
});

Utilisation dans les templates


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

// Limiter au group1 avec le role admin ou editor
<template name="header">
  {{#if isInRole 'admin,editor' 'group1'}}
    {{> editor_stuff}}
  {{/if}}
</template>

Collections

  • Meteor.roles
  • Modifie Meteor.users

Ajout de role

  • users Array | String
  • roles Array | String
  • group String optional (default Roles.GLOBAL_GROUP)

addUsersToRoles vs setUserRoles: addUsersToRoles: ajoute un ou plusieurs roles setUserRoles: affecte ou remplace les roles

Roles.addUsersToRoles(userId, 'admin')

Roles.addUsersToRoles(userId, ['view-secrets'], 'example.com')

Roles.addUsersToRoles([user1, user2], ['user','editor'])

Roles.addUsersToRoles([user1, user2], ['glorious-admin', 'perform-action'], 'example.org')

Roles.addUsersToRoles(userId, 'admin', Roles.GLOBAL_GROUP)

Extension ongoworks-security

  • https://atmospherejs.com/ongoworks/security
  • https://github.com/reactioncommerce/reaction-core/blob/5d719c2a74ea6c0e125ad5113af39859f697bf03/server/security.coffee
$ meteor add ongoworks:security

Affecte des permissions par collection

// server/security.js:

// Any client may insert, update, or remove a post without restriction
Posts.permit(['insert', 'update', 'remove']).apply();

// No clients may insert, update, or remove posts
Posts.permit(['insert', 'update', 'remove']).never().apply();

// Clients may insert posts only if a user is logged in
Posts.permit('insert').ifLoggedIn().apply();

// Clients may remove posts only if an admin user is logged in
Posts.permit('remove').ifHasRole('admin').apply();

// Admin users may update any properties of any post, but regular users may
// update posts only if they don't try to change the `author` or `date` properties
Posts.permit('update').ifHasRole('admin').apply();
Posts.permit('update').ifLoggedIn().exceptProps(['author', 'date']).apply();

Méthodes chainables:

never() - Jamais

ifLoggedIn() - Si l'utilisateur est authentifié

ifHasUserId(userId) - N'autorise que le userid

ifHasRole(role) - Autorise si l'utilisateur à le role

ifHasRole({role: 'admin', group: 'mygroup'}

ifHasRole({role: role, group: group})

onlyProps(props) - N'autorise que la modification de ces champs

exceptProps(['owner', 'owner_group'])

exceptProps(props) - ?

Personnalisation de methods


// usage: Posts.permit('remove').ifLoggedIn().apply();
Security.defineMethod("ifLoggedIn", {
  fetch: [],
  transform: null,
  deny: function (type, arg, userId) {
    return !userId;
  }
});

// ifHasUserId
Security.defineMethod("ifHasUserId", {
  fetch: [],
  transform: null,
  deny: function (type, arg, userId) {
    return userId !== arg;
  }
});

Security.defineMethod("ifIsCurrentUser", {
  fetch: [],
  transform: null,
  deny: function (type, arg, userId, doc) {
    return userId !== doc._id;
  }
});

userIsInRole

Roles.userIsInRole(Meteor.userId(), ["admin"]);

getAllRoles

JSON.stringify(Roles.getAllRoles().fetch(), null, '\t')
"[
    {
        "_id": "us7DSDJsijRsmSQzq",
        "name": "admin"
    },
    {
        "_id": "JajeKHFCQMRYqvYX6",
        "name": "client"
    },
    {
        "_id": "zpH3Q76KDFYhMsSNB",
        "name": "manager"
    },
    {
        "_id": "if4QYbtof6mAh3RYn",
        "name": "user"
    }
]"