Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ If you wondering if there are currently any tables without policies, you can che
`SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND NOT rowsecurity`.


## Database user/role permissions

If the database user used for your application has either `SUPERUSER` or `BYPASSRLS`, then RLS policies will not be enforced. You can remove these attributes from your database user, but a caution that you may need to tweak other permissions so database migrations and other typical "superuser" tasks still work.

Alternatively, you can set `config.unprivileged_db_role` to another database role that does not have these attributes. Your primary database role will still be used for migrations, but as soon as a tenant is set, the role is switched for that session. This also avoids the issue of a table owner bypassing RLS by default.


## Future Work

### Testing Policies
Expand Down
1 change: 1 addition & 0 deletions lib/generators/rls_rails/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def create_initializer
config.tenant_class = Tenant
config.tenant_fk = :tenant_id
config.policy_dir = 'db/policies'
config.unprivileged_db_role = nil
end
RUBY
end
Expand Down
1 change: 1 addition & 0 deletions lib/generators/rls_rails/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def create_initializer
config.tenant_class = Tenant
config.tenant_fk = :tenant_id
config.policy_dir = 'db/policies'
config.unprivileged_db_role = nil
end
RUBY
end
Expand Down
22 changes: 21 additions & 1 deletion lib/rls_rails/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def self.disable!

clear_query_cache
execute_sql("SET SESSION rls.disable = TRUE;")
set_role(privileged: true)
@rls_status.merge!(disabled: 'true')
debug_print "WARNING: ROW LEVEL SECURITY DISABLED!\n"
end
Expand All @@ -26,6 +27,7 @@ def self.enable!

clear_query_cache
debug_print "ROW LEVEL SECURITY ENABLED!\n"
set_role(privileged: false)
execute_sql("SET SESSION rls.disable = FALSE;")
@rls_status.merge!(disabled: 'false')
end
Expand All @@ -41,6 +43,7 @@ def self.set_tenant tenant
clear_query_cache
debug_print "Accessing database as #{tenant.name}\n"
execute_sql "SET SESSION rls.disable = FALSE; SET SESSION rls.tenant_id = #{tenant.id};"
set_role(privileged: false)
@rls_status.merge!(tenant_id: tenant.id.to_s)
end

Expand All @@ -51,6 +54,7 @@ def self.set_user user
clear_query_cache
debug_print "Accessing database as #{user.class}##{user.id}\n"
execute_sql "SET SESSION rls.disable = FALSE; SET SESSION rls.user_id = #{user.id};"
set_role(privileged: false)
@rls_status.merge!(user_id: user.id.to_s)
end

Expand All @@ -70,6 +74,7 @@ def self.reset!
RESET rls.tenant_id;
RESET rls.disable;
SQL
set_role(privileged: false)
clear_query_cache
@rls_status.merge!(tenant_id: '', user_id: '', disabled: '')
end
Expand All @@ -89,6 +94,7 @@ def self.status= status
SET SESSION rls.user_id = '#{user_id}';
SET SESSION rls.tenant_id = '#{tenant_id}';
SQL
set_role(privileged: status[:disable] && status[:disable] != 'false')
@rls_status.merge!(tenant_id: tenant_id, user_id: user_id, disabled: disable)
end

Expand Down Expand Up @@ -150,6 +156,16 @@ def self.run_per_tenant &block
end
end

def self.set_role(privileged: false)
return unless unprivileged_db_role.present?

if privileged
execute_sql('SET ROLE NONE;')
else
execute_sql("SET ROLE #{unprivileged_db_role};")
end
end

def self.tenant_class
Railtie.config.rls_rails.tenant_class
end
Expand All @@ -158,6 +174,10 @@ def self.user_class
Railtie.config.rls_rails.user_class
end

def self.unprivileged_db_role
Railtie.config.rls_rails.unprivileged_db_role
end

private

def self.clear_query_cache
Expand All @@ -171,4 +191,4 @@ def self.execute_sql query
def self.debug_print s
print s if Railtie.config.rls_rails.verbose
end
end
end
1 change: 1 addition & 0 deletions lib/rls_rails/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Railtie < ::Rails::Railtie
config.rls_rails.user_class = nil
config.rls_rails.tenant_fk = :tenant_id
config.rls_rails.verbose = false
config.rls_rails.unprivileged_db_role = nil

initializer "rls_rails.load" do
ActiveSupport.on_load :active_record do
Expand Down