Structure role-management in meteor-app with alanning:roles
Asked Answered
S

1

6

I need some advice for building a correct role schema and management in my meteor-app.

Structure

  • Im using alanning:[email protected] for adding role management functionallity to the app.
  • There are four different user-types: Admin, Editor, Expert and User.
  • Furthermore there are several modules with different content, i.e. Cars, Maths and Images. Every module is organized in an own meteor-package.
  • In every module there are several categories, which can be added dynamically by editors.

Categories in modules

Module is structured like this:

elementSchema = new SimpleSchema({ 
    element:    {type: String, optional: true}
});

Cars.attachSchema(new SimpleSchema({
    title:      { type: String },
    content:    { type: String },
    category:   { type: [elementSchema], optional: true },
});

As you can see, all available categories are inside of the Collection of the module.

Rights

  • Admin: Complete rights
  • Editor: Can edit elements in selected moduls (i.e. editor_1 can edit elements in Cars and Images but not for Maths)
  • Expert: Can get rights to a complete module or just to some categories of a module (i.e.) expert_1 can edit Images, but only the elements in category "Honda" and "Mercedes" in Cars; no editing to Maths)
  • User: No editing

This is how I do the authentification technically:

router.js

var filters = {
    authenticate: function () {
        var user;
        if (Meteor.loggingIn()) {
            this.layout('login');
            this.render('loading');
        } else {
            user = Meteor.user();
            if (!user) {
                this.layout('login');
                this.render('signin');
                return;
            }
            this.layout('Standard');
            this.next();
        }
    }
}
Router.route('/car/:_id', {
    name: 'car',
    before: filters.authenticate,
    data: function () {
        return { 
            cars: Cars.findOne({ _id: this.params._id }) 
        };
    }
});

template

<template name="car">
    {{#if isInRole 'cars'}}
        Some form for editing
    {{else}}
        <h1>Restricted area</h1>
    {{/if}}
</template>

I put this router.js to every package. Only change is the data function which uses the Collection of each package (Cars, Maths, Images).

Update: As 'Eliezer Steinbock' commented it is necessary to restrict acces to the mongoDB itself. But until now I only did that on the routes.

permissions.js

Cars.allow({
    insert: function(userId) {
        var loggedInUser = Meteor.user()
        if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
    },
    update: function(userId) {
        var loggedInUser = Meteor.user()
        if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
    }
});

My problems

1) My first problem is how to use roles and groups. What would be the best way for using groups? And the second problem is, that there are no fixed categories in the modules. Right now I have no idea for a useful role/group schema.

2) How do I check for the roles? As there are different roles which can get access: admin, editor and expert. Also I got the problem with these experts who just get access to defined categories of this module.

3) Wouldn't it be better to make the permission.js more general. I mean, is it possible to make a dynamic function, so I don't have to put everywhere the same code? How do I implement the roles in the permission.js in a useful way?

Secco answered 16/9, 2015 at 12:30 Comment(3)
Where roles are most important is for editing collections (insert/update/delete) and subscribing to data. Restricting access to a certain route doesn't stop users getting at the data. Use roles to restrict access in the appropriate places (i.e. Meteor methods, allow/deny, and publications)Shalandashale
Thanks for the comment. I added the part, which I wrote so far. But it is far away from perfect.Secco
Your first and third question are not on-topic on Stack Overflow since they are opinion-based or too broad. Your second question lacks a precise MCVE.Timaru
S
7

if the logic for the permissions is the same you could just define it once in permissions.js

App = App || {}; // We are using Namespaces, so you don't have to.. but it's good
App.Permissions = {
    insert: function(userId) {
        var loggedInUser = Meteor.user()
        if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
    },
    update: function(userId) {
        var loggedInUser = Meteor.user()
        if (loggedInUser && Roles.userIsInRole(loggedInUser, ['admin','editor'])) return true;
    }
}

And then you can use it for your Collections:

Cars.allow(App.Permissions); // Or
Cars.allow(App.Permissions.getPermissionsForGroup('cars'))

Define roles somewhere..

Roles

// Give user the role "editor" in "cars" group
Roles.addUsersToRoles(someUserId, ['editor'], 'cars');
Roles.addUsersToRoles(someOtherId, ['admin'], 'cars');

Which you can prepare in permissions.js like this:

Permissions

App = App || {}; 
App.Permissions = {
 insert: function(userId) {...},
 update: function(userId) {...},
 getPermissionsForGroup: function(group) {
    return {
       insert: function(userId, doc) {
          // Only admin can insert
          return Roles.userIsInRole(userId, "admin", group);
       },
       update: function(userId, doc, fields, modifier) {
          // Editor & Admin can edit
          return Roles.userIsInRole(userId, ["editor","admin"], group);
       },
       remove: function(userId, doc) {
          // Only admin can remove
          return Roles.userIsInRole(userId, "admin", group);
       }
    }    
}

In this example admins can insert and update.. and editors can only update, but insert.

Regarding the documentation of alanning:roles you define and use roles like this:

// Super Admin definition..
Roles.addUsersToRoles(superAdminId, ['admin'], Roles.GLOBAL_GROUP);

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

Yeah, make sure, that the permission logic will be included before your Collection definition.. obviously :)

Southerly answered 21/9, 2015 at 10:29 Comment(5)
Shouldn't MyApp.Permision in you example be App.Permission?Secco
For getSpecialPermission(group) { I get the error Unexpected token (. And the last thing: Do you have an idea to connect the getSpecialPermission to the existing categories (Cars.attachSchema)?Secco
@Secco just fixed the code.. you have to imagine, that this is only an example how you could do it.. to give you an idea how to structure it that you don't have to rewrite every allow object and keep it in one place, right?Southerly
@Secco you don't need to attach the schema to permission, this are too different things.. you schema should be in your /both/cars_collection.js together with your collection definition.. schema is schema, permissions are permissionsSoutherly
Thanks for showing how can put everything on one place. That is very helpful. I know that schema and permissions are something different. My question is just how to define roles, which depends on categories. As a editor can edit all Cars, but an expert can just edit some categories of Cars. That's the last tricky thing I have to do. But the categories are not fixed. They can be extended by an editor.Secco

© 2022 - 2024 — McMap. All rights reserved.