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 166 forks source link

Multitenancy and roles #265

Closed jamauro closed 4 years ago

jamauro commented 5 years ago

For those using this package as part of your multitenant Meteor app, I'd love to hear how you went about it.

Being newish to Meteor, I'm still trying to wrap my head around the best way to architect multitenancy.

Reading this issue helped a bit: https://github.com/alanning/meteor-roles/issues/158

alanning commented 5 years ago

Hi @jamauro, yes #158 is a great resource on multi-tenancy with roles.

Generally I recommend a separate field on the User record for keeping track of which accounts a user is a "member" of. The way Roles stores its data in the database makes it more useful for just tracking permissions rather than membership as well. With Roles 2.0, you may be able to just use the Roles data to query but I'm not familiar enough with it to know for sure.

With our app using Roles v1, we have an array field on the user record which stores unique ids for which "network" the user is a member of. Then we use Roles to control permissions for each of those networks. We use the unique Network ID as the Group ID for Roles.

Ultimately you just want the Group IDs to be unique to a specific customer. You can either maintain a collection/table of mapping groups to customers or you can do something like using the unique Customer ID as a prefix for the Group ID.

Say you have a collection called CustomerAccounts. You can have each CustomerAccount record have a list of GroupIDs that belong to that account so you can easily query when you want to run reports or something.

Example:

CustomerAccount: {
  customerId: 'acme',
  name: 'Acme',
  groups: ['acme/chicago', 'acme/new york', 'acme/los angeles']
}

Or...

CustomerAccount: {
  customerId: 'acme',
  name: 'Acme',
  groups: ['acme/store/1234', 'acme/store/1385', 'acme/region/2']
}

Another way would be to have a CustomerAccount not include the groups and store that info in your Stores collection; just need something that points back to the proper customer account:

CustomerAccount: {
  customerId: 'acme',
  name: 'Acme'
}

Store: {
  storeId: '1234',
  customerId: 'acme'
}

At my startup, we use the 2nd, bottom-up method with prefixing. Either way the Group ID would still be something like, "acme/store/1234".