Table of Contents generated with DocToc
Philia is a port of Milia, a multi-tenanting gem for Ruby on Rails applications. Philia supports (and requires) Devise.
A tenant is an organization with many members (users). Initially a user creates a new organization (tenant) and becomes its first member (and usually admin). Then he invites further members who can then login and join the tenant. Philia ensures that users can only access data of their own tenant (organization).
Models which belong to a certain tenant (organization).
Add acts_as_tenant to the model body to activate tenanting for this model.
Most of your tables (except for pure join tables, users, and tenants) should be tenanted.
Every record of a tenanted table needs to have a tenant_id
set. Philia takes care of this.
Models which aren't specific to a tenant (organization) but have system wide relevance.
Add acts_as_universal to the model body to mark them as universal models.
Universal tables never contain critical user/company information.
The devise user table must be universal and should only contain email, encrypted password, and devise-required data.
All other user data (name, phone, address, etc) should be broken out into a tenanted table called members
(Member belongs_to :user
, User has_one :member
).
The same applies for organization (account or company) information.
A record of a universal table must have tenant_id
set to nil. Philia takes care of this.
Pure join tables (has_and_belongs_to_many HABTM associations) are neither Universal nor Tenanted.
Add this line to your application's Gemfile:
gem 'philia', github: 'philsmy/philia', branch: 'main'
And then execute:
$ bundle
Or install it yourself as:
$ gem install philia
Build Sample App (have Rails 6 as your default rails)
rails new test-philia-app --database=mysql -T
cd test-philia-app
bundle add devise
echo "gem 'philia', path: '../philia'" >> Gemfile
bundle
rails g philia:install
rails db:drop db:create db:migrate
As this is usually done as part of a Rails5 to Rails6 upgrade there's obviously a TON more you need to do. But for this specific gem I have found this to work:
- search
Milia
, replace withPhilia
(I do this in all*.rb
files) - search
milia
, replace withphilia
(I do this in all*.rb
files) - rename
config/initializers/milia.rb
toconfig/initializers/philia.rb
That should get you the bulk of the way there.
- Philia designates a default_scope for all models (both universal and tenanted). Rails merges default_scopes if you use multiple default_scope declarations in your model, see ActiveRecord Docs. However by unscoping via unscoped you can accidentally remove tenant scoping from records. Therefore we strongly recommend to NOT USE default_scope at all.
- Philia uses Thread.current[:tenant_id] to hold the current tenant for the existing Action request in the application.
- SQL statements executed outside the context of ActiveRecord pose a potential danger; the current philia implementation does not extend to the DB connection level and so cannot enforce tenanting at this point.
- The tenant_id of a universal model will always be forced to nil.
- The tenant_id of a tenanted model will be set to the current_tenant of the current_user upon creation.
- HABTM (has_and_belongs_to_many) associations don't have models; they shouldn't have id fields (setup as below) nor any field other than the joined references; they don't have a tenant_id field; rails will invoke the default_scope of the appropriate joined table which does have a tenant_id field.
- Your code should never try to change or set the
tenant_id
of a record manually.- philia will not allow it
- philia will check for deviance
- philia will raise exceptions if it's wrong and
- philia will override it to maintain integrity.
- You use philia solely at your own risk!
- When working with multi-tenanted applications you handle lots of data of several organizations/companies which means a special responsibility for protecting the data as well. Do in-depth security tests prior to publishing your application.
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
- Fork the project
- Start a feature/bugfix branch
- Commit and push until you are happy with your contribution
- Make sure to add tests for it. This is important so we don't break the feature in a future version unintentionally.
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so we can cherry-pick around it.
My name is Phil.
The gem is available as open source under the terms of the MIT License.