Meteor-Community-Packages / meteor-roles

Authorization package for Meteor, compatible with built-in accounts packages
http://meteor-community-packages.github.io/meteor-roles/
MIT License
921 stars 168 forks source link

User creates group name on signup #158

Closed allenfuller closed 8 years ago

allenfuller commented 8 years ago

I'm really excited about using this package. One question I have for a multi-tenant SaaS environment is how could a user create a group?

I have in mind a signup workflow like this, where when the user signs up they provide their:

Then, upon successful signup, they have an account for their organization.

I think I can take it from there with your documentation and examples, I'm just not sure how to let the user create the group name in the first place (and then assign them an 'admin' role, of course).

Thanks!

alanning commented 8 years ago

Hi Allen, nice to hear of your interest!

Group creation is actually done implicitly once the user is granted permission (added to a role) in that group.

So doing this:

Roles.addUsersToRoles(joesUserId, 'admin', 'acme.com')

... will create a field on Joe's user record like this:

{
  _id: joesUserId,
  roles: {
    'acme_com': ['admin']
  }
}

The 'acme.com' group name is just a key for the package (or you) to reference which roles you care about when performing your checks.

Roles v2 will probably have some more formal group creation process since one of our goals is to support permission hierarchies.

One other thing I'd like to mention is that the roles package doesn't explicitly track "membership" in a group, only the permissions users have in that group. With roles v1 its best to use another field on the user object to track membership. So for example:

{
  _id: joesUserId,
  companies: ['acme.com'],
  roles: {...}
}

Be sure to index your membership field so that your queries don't need to do a full collection scan.

allenfuller commented 8 years ago

@alanning, thanks! That's incredibly helpful feedback. Looking forward to digging into it.

allenfuller commented 8 years ago

@alanning -- thanks again for your advice the other day! I've almost got everything wired up. The spot where I'm hung up now is adding the user's organization to the isInRole handlebar statement. Here's what I'm trying to do:

  <ul class="nav navbar-nav navbar-right nav-desktop hidden-xs">
    <li role="presentation" class="dropdown">
      <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{currentUser.emails.[0].address}} <span class="caret"></span></a>
      <ul class="dropdown-menu" role="menu">
        <li><a href="{{pathFor 'user'}}">My Profile</a></li>
      {{#if isInRole 'admin' '{{userOrg}}'}}
        <li><a href="{{pathFor 'users'}}">Manage Users</a></li>
        <li><a href="{{pathFor 'settings'}}">Account Settings</a></li>
      {{/if}}
        <li class="logout"><a href="#">Logout</a></li>
      </ul>
    </li>
  </ul>

So that only an admin will see certain fields.

The {{userOrg}} statement is defined in the template's helper:

Template.navMain.helpers({
  userOrg: function() {
    return Meteor.user() && Meteor.user().organizations;
  }
});

(Based on the Meteor.user().companies suggestion you gave yesterday.)

Is there a way to pass a handlebars statement within the isInRole statement, or is there a better way to accomplish this (so I'm passing only the current group and role to the statement)?

Thanks again!

alanning commented 8 years ago

I'd suspect the issue is that isInRole (and Roles.userIsInRole which it relies on) expect the group param to be a singular, string field. In your case I would guess its coming through as something like: '["acme.com"]' since organizations is probably an array.

In this case, you should first determine what you want to happen when a user is an admin in one org but not another. Should the menu item still be displayed?

In our application, we handle this by not displaying the menu item until the user has chosen an "organization". Then we perform the admin check one that specific organization rather than on all of the organizations that the user has access to.

allenfuller commented 8 years ago

Thanks for the feedback. Organizations is an array, but when I just use that handlebars statement elsewhere in the template, it displays as a string. So I'll tinker with that.

Right now, I'm setting the organization when the user signs up. So if they create a new organization, they're automatically added to the role 'admin' for that organization, and the organization field is created. Later I was going to go back and add an organization selection to allow for multiple orgs, and then set a session variable based on that selection and let the session variable drive all the publications and organization-specific features.

Is there a better way to deliver the group name to the isInRole where the user is an admin dynamically?

I think I can do that via a session variable tied to a publication right? Sorry, that's probably outside the scope of support you can provide. But I feel like I'm always just one semi-colon away from success... :)

alanning commented 8 years ago

Closing this issue but if you'd like to post a link to a public repo I can try helping further.

allenfuller commented 8 years ago

I think I'm all set now, thanks! I ended up creating a separate field on the user collection for currentOrg. Then that's the field I check before showing any admin-related fields in isInRole.

This is such a great package! Thanks again!