Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rls_tenant

Postgres row-level-security multi-tenancy for Rails and ActiveRecord.

rls_tenant makes every database query tenant aware at the source. You set the active tenant once per request and Postgres enforces isolation for the rest of the request. A forgotten where clause cannot leak another tenant's rows, because the database itself filters them out.

It gives you three pieces that fit together:

  • A per-request tenant switcher that pushes the active tenant into the database session.
  • A model concern that stamps new rows with the tenant and adds readable tenant scopes.
  • A migration helper that enables and FORCEs row level security with a tenant isolation policy.

How it works

rls_tenant speaks the standard Postgres RLS mechanism. RLS policies read a session setting (a GUC) with current_setting. This gem writes that setting at the start of each tenant scope using set_config(name, value, true), so the value is local to the transaction and never leaks across pooled connections. Your migration installs a policy that compares each row's tenant column against that setting. Because the policy is FORCEd, it applies even to the table owner, so application code cannot accidentally bypass it.

Installation

Add it to your Gemfile:

gem "rls_tenant"

Then run:

bundle install

Configuration

# config/initializers/rls_tenant.rb
RlsTenant.configure do |config|
  config.tenant_setting = "app.current_tenant" # the Postgres setting policies read
  config.tenant_column  = "tenant_id"          # the column on tenant-scoped tables
end

The defaults shown above work out of the box.

Enabling RLS in a migration

class EnableRlsOnInvoices < ActiveRecord::Migration[7.1]
  include RlsTenant::Migration

  def up
    enable_rls :invoices
  end

  def down
    disable_rls :invoices
  end
end

enable_rls turns on RLS, FORCEs it, and creates a policy named invoices_tenant_isolation that matches rows whose tenant_id equals the current tenant setting, for both reads (USING) and writes (WITH CHECK).

You can override the column, policy name, or setting per table:

enable_rls :bills, column: "org_id", setting: "app.org"

Making a model tenant aware

class Invoice < ApplicationRecord
  include RlsTenant::Model
end

New records are stamped with the active tenant id automatically, and you get two scopes:

Invoice.for_current_tenant      # rows for the active tenant
Invoice.for_tenant(account.id)  # rows for a specific tenant

The hard guarantee comes from the database policy; these scopes keep your Ruby code expressive.

Setting the tenant per request

Wrap each request in with_tenant. In Rails this is typically an around_action:

class ApplicationController < ActionController::Base
  around_action :scope_to_tenant

  private

  def scope_to_tenant(&block)
    RlsTenant.with_tenant(current_account.id, &block)
  end
end

Inside the block, every query and write is scoped to that tenant by Postgres:

RlsTenant.with_tenant(account.id) do
  Invoice.create!(amount_cents: 5_000) # stamped + visible only to this tenant
  Invoice.count                        # counts only this tenant's invoices
end

with_tenant nests cleanly. The previous tenant is restored when the block returns, even if it raises.

The standard it speaks

This gem is a thin, idiomatic layer over native Postgres row level security:

  • Policies read a session GUC via current_setting(name, true).
  • The gem sets that GUC with set_config(name, value, true) (transaction local).
  • Tables use ENABLE plus FORCE ROW LEVEL SECURITY.

If you already know Postgres RLS, there is nothing new to learn. If you do not, the migration helper writes the correct policy for you.

Development

bundle install
bundle exec rspec

License

MIT. See LICENSE.

About

Postgres row-level-security multi-tenancy for Rails and ActiveRecord. Set the tenant once per request and the database enforces isolation for every query.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages