pg_rls 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/pg_rls/version.rb +1 -1
- metadata +1 -37
- data/.rspec +0 -3
- data/LICENSE.txt +0 -21
- data/lib/generators/pg_rls/active_record/templates/abstract_base_class.rb.tt +0 -9
- data/lib/generators/pg_rls/active_record/templates/convert_migration.rb.tt +0 -11
- data/lib/generators/pg_rls/active_record/templates/convert_migration_backport.rb.tt +0 -12
- data/lib/generators/pg_rls/active_record/templates/init_convert_migration.rb.tt +0 -11
- data/lib/generators/pg_rls/active_record/templates/init_migration.rb.tt +0 -25
- data/lib/generators/pg_rls/active_record/templates/init_model.rb.tt +0 -24
- data/lib/generators/pg_rls/active_record/templates/migration.rb.tt +0 -17
- data/lib/generators/pg_rls/active_record/templates/model.rb.tt +0 -24
- data/lib/generators/pg_rls/base.rb +0 -36
- data/lib/generators/pg_rls/install_generator.rb +0 -90
- data/lib/generators/pg_rls.rb +0 -19
- data/lib/generators/templates/README +0 -22
- data/lib/generators/templates/pg_rls.rb.tt +0 -48
- data/lib/pg_rls/Rakefile +0 -7
- data/lib/pg_rls/current/context.rb +0 -10
- data/lib/pg_rls/database/admin_statements.rb +0 -28
- data/lib/pg_rls/database/configurations.rb +0 -46
- data/lib/pg_rls/database/prepared.rb +0 -40
- data/lib/pg_rls/database/tasks/admin_database.rake +0 -40
- data/lib/pg_rls/errors/index.rb +0 -4
- data/lib/pg_rls/errors/rake_only_error.rb +0 -12
- data/lib/pg_rls/errors/tenant_not_found.rb +0 -13
- data/lib/pg_rls/logger.rb +0 -31
- data/lib/pg_rls/middleware/set_reset_connection.rb +0 -93
- data/lib/pg_rls/middleware/sidekiq/client.rb +0 -22
- data/lib/pg_rls/middleware/sidekiq/server.rb +0 -19
- data/lib/pg_rls/middleware/sidekiq.rb +0 -11
- data/lib/pg_rls/middleware.rb +0 -8
- data/lib/pg_rls/multi_tenancy.rb +0 -32
- data/lib/pg_rls/schema/down_statements.rb +0 -54
- data/lib/pg_rls/schema/dumper.rb +0 -36
- data/lib/pg_rls/schema/statements.rb +0 -72
- data/lib/pg_rls/schema/up_statements.rb +0 -104
- data/lib/pg_rls/tenant.rb +0 -153
data/lib/pg_rls/errors/index.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
module Errors
|
5
|
-
# Raise Tenant Not found and ensure that the tenant is resetted
|
6
|
-
class TenantNotFound < StandardError
|
7
|
-
def initialize(msg = nil)
|
8
|
-
msg ||= "Tenant Doesn't exist"
|
9
|
-
super
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
data/lib/pg_rls/logger.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
module PgRls
|
6
|
-
class Logger
|
7
|
-
def initialize
|
8
|
-
@logger = ::Logger.new($stdout) # You can output to a file if needed
|
9
|
-
@logger.level = ::Logger::DEBUG
|
10
|
-
end
|
11
|
-
|
12
|
-
def log(message, level: :info)
|
13
|
-
case level
|
14
|
-
when :debug
|
15
|
-
@logger.debug(message)
|
16
|
-
when :info
|
17
|
-
@logger.info(message)
|
18
|
-
when :warn
|
19
|
-
@logger.warn(message)
|
20
|
-
when :error
|
21
|
-
@logger.error(message)
|
22
|
-
else
|
23
|
-
@logger.info(message)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def deprecation_warning(message)
|
28
|
-
log("[DEPRECATION WARNING]: #{message}", level: :warn)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
class FrozenConfiguration < StandardError; end
|
5
|
-
|
6
|
-
def self.sessions
|
7
|
-
@sessions ||= Redis.new(@session_store_server)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.session_prefix
|
11
|
-
@session_prefix ||= begin
|
12
|
-
store_default_warden_key = @session_store_default_warden_key || '2'
|
13
|
-
|
14
|
-
"#{session_key_prefix}:#{store_default_warden_key}::"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.session_store_server=(opts = {})
|
19
|
-
raise Errors::FrozenConfiguration unless @sessions.nil?
|
20
|
-
|
21
|
-
@session_store_server = opts.deep_symbolize_keys
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.session_store_default_warden_key=(val)
|
25
|
-
raise Errors::FrozenConfiguration unless @sessions.nil?
|
26
|
-
|
27
|
-
@session_store_default_warden_key = val
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.session_key_prefix
|
31
|
-
@session_key_prefix ||= @session_key_prefix || @session_store_server[:key_prefix]
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.session_key_prefix=(val)
|
35
|
-
raise Errors::FrozenConfiguration unless @sessions.nil?
|
36
|
-
|
37
|
-
@session_key_prefix = val
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
module PgRls
|
42
|
-
module Middleware
|
43
|
-
# Set RLS if sessions present.
|
44
|
-
class SetResetConnection
|
45
|
-
def initialize(app)
|
46
|
-
@app = app
|
47
|
-
end
|
48
|
-
|
49
|
-
def call(env)
|
50
|
-
tenant = load_tenant_thought_session(env)
|
51
|
-
|
52
|
-
return @app.call(env) if tenant.blank?
|
53
|
-
|
54
|
-
PgRls::Tenant.with_tenant!(tenant) { @app.call(env) }
|
55
|
-
rescue PgRls::Errors::TenantNotFound
|
56
|
-
@app.call(env)
|
57
|
-
rescue ActiveRecord::RecordNotFound => e
|
58
|
-
raise e unless rails_active_storage_request?(env)
|
59
|
-
|
60
|
-
[404, { 'Content-Type' => 'text/plain' }, ['Could not find asset']]
|
61
|
-
end
|
62
|
-
|
63
|
-
def load_session_cookie_value(env)
|
64
|
-
cookie_string = env['HTTP_COOKIE']
|
65
|
-
return if cookie_string.nil?
|
66
|
-
|
67
|
-
cookie_regex = /#{PgRls.session_key_prefix}=([^;]+)/
|
68
|
-
match = cookie_regex.match(cookie_string)
|
69
|
-
match[1] if match
|
70
|
-
end
|
71
|
-
|
72
|
-
def load_tenant_thought_session(env)
|
73
|
-
cookie = load_session_cookie_value(env)
|
74
|
-
|
75
|
-
return if cookie.blank?
|
76
|
-
|
77
|
-
redis_session_key = "#{PgRls.session_prefix}#{Digest::SHA256.hexdigest(cookie)}"
|
78
|
-
tenant_session = Marshal.load(PgRls.sessions.get(redis_session_key))
|
79
|
-
|
80
|
-
return if tenant_session.blank?
|
81
|
-
return if tenant_session['_tenant'].blank?
|
82
|
-
|
83
|
-
tenant_session['_tenant'] if tenant_session.present?
|
84
|
-
rescue TypeError
|
85
|
-
nil
|
86
|
-
end
|
87
|
-
|
88
|
-
def rails_active_storage_request?(env)
|
89
|
-
env['PATH_INFO'].start_with?('/rails/active_storage/')
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# :nocov:
|
4
|
-
module PgRls
|
5
|
-
module Middleware
|
6
|
-
module Sidekiq
|
7
|
-
# Set PgRls Policies
|
8
|
-
class Client
|
9
|
-
def call(_job_class, msg, _queue, _redis_pool)
|
10
|
-
load_tenant_attribute!(msg)
|
11
|
-
yield
|
12
|
-
end
|
13
|
-
|
14
|
-
def load_tenant_attribute!(msg)
|
15
|
-
return msg['admin'] = true if PgRls.admin_connection?
|
16
|
-
|
17
|
-
msg['pg_rls'] ||= PgRls::Tenant.fetch&.id
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# :nocov:
|
4
|
-
module PgRls
|
5
|
-
module Middleware
|
6
|
-
module Sidekiq
|
7
|
-
# Set PgRls Policies
|
8
|
-
class Server
|
9
|
-
def call(_job_instance, msg, _queue, &)
|
10
|
-
if msg['admin']
|
11
|
-
PgRls.admin_execute(&)
|
12
|
-
else
|
13
|
-
PgRls::Tenant.with_tenant!(msg['pg_rls'], &)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/lib/pg_rls/middleware.rb
DELETED
data/lib/pg_rls/multi_tenancy.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
# Ensure Connection is with App_use
|
5
|
-
module MultiTenancy
|
6
|
-
def self.included(base)
|
7
|
-
base.class_eval do
|
8
|
-
around_action :switch_tenant!
|
9
|
-
|
10
|
-
def current_tenant
|
11
|
-
@current_tenant ||= request.subdomain
|
12
|
-
end
|
13
|
-
helper_method :current_tenant
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def switch_tenant!
|
20
|
-
fetched_tenant = session[:_tenant] || current_tenant
|
21
|
-
return yield if PgRls::Tenant.fetch.present?
|
22
|
-
|
23
|
-
Tenant.with_tenant!(fetched_tenant) do |tenant|
|
24
|
-
session[:_tenant] = tenant.id
|
25
|
-
yield(tenant)
|
26
|
-
end
|
27
|
-
rescue NoMethodError
|
28
|
-
session[:_tenant] = nil
|
29
|
-
raise PgRls::Errors::TenantNotFound, 'No tenant was found'
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
module Schema
|
5
|
-
# Down Schema Statements
|
6
|
-
module DownStatements
|
7
|
-
def drop_rls_user
|
8
|
-
ActiveRecord::Migration.execute <<~SQL.squish
|
9
|
-
DROP OWNED BY #{PgRls.username};
|
10
|
-
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM #{PgRls.username};
|
11
|
-
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM #{PgRls.username};
|
12
|
-
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM #{PgRls.username};
|
13
|
-
DROP USER #{PgRls.username};
|
14
|
-
SQL
|
15
|
-
end
|
16
|
-
|
17
|
-
def drop_rls_blocking_function
|
18
|
-
ActiveRecord::Migration.execute 'DROP FUNCTION IF EXISTS id_safe_guard ();'
|
19
|
-
end
|
20
|
-
|
21
|
-
def drop_rls_setter_function
|
22
|
-
ActiveRecord::Migration.execute 'DROP FUNCTION IF EXISTS tenant_id_setter ();'
|
23
|
-
end
|
24
|
-
|
25
|
-
def detach_blocking_function(table_name)
|
26
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
27
|
-
DROP TRIGGER IF EXISTS id_safe_guard
|
28
|
-
ON #{table_name};
|
29
|
-
SQL
|
30
|
-
end
|
31
|
-
|
32
|
-
def detach_trigger_function(table_name)
|
33
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
34
|
-
DROP TRIGGER IF EXISTS tenant_id_setter
|
35
|
-
ON #{table_name};
|
36
|
-
SQL
|
37
|
-
end
|
38
|
-
|
39
|
-
def drop_rls_column(table_name)
|
40
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
41
|
-
ALTER TABLE #{table_name}
|
42
|
-
DROP COLUMN IF EXISTS tenant_id;
|
43
|
-
SQL
|
44
|
-
end
|
45
|
-
|
46
|
-
def drop_rls_policy(table_name)
|
47
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
48
|
-
DROP POLICY #{table_name}_#{PgRls.username} ON #{table_name};
|
49
|
-
ALTER TABLE #{table_name} DISABLE ROW LEVEL SECURITY;
|
50
|
-
SQL
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
data/lib/pg_rls/schema/dumper.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
module Schema
|
5
|
-
module Dumper
|
6
|
-
def table(table, stream)
|
7
|
-
temp_stream = StringIO.new
|
8
|
-
super(table, temp_stream)
|
9
|
-
temp_stream_string = temp_stream.string
|
10
|
-
if rls_tenant_table?(table)
|
11
|
-
temp_stream_string.gsub!('create_table', 'create_rls_tenant_table')
|
12
|
-
elsif rls_table?(table)
|
13
|
-
temp_stream_string.gsub!('create_table', 'create_rls_table')
|
14
|
-
end
|
15
|
-
|
16
|
-
stream.print(temp_stream_string)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def rls_table?(table_name)
|
22
|
-
# Logic to determine if the table uses RLS
|
23
|
-
# You can check if the table has RLS policies or use a naming convention
|
24
|
-
@connection.execute(<<-SQL.squish).any?
|
25
|
-
SELECT 1 FROM pg_policies WHERE tablename = #{ActiveRecord::Base.connection.quote(table_name)};
|
26
|
-
SQL
|
27
|
-
end
|
28
|
-
|
29
|
-
def rls_tenant_table?(table_name)
|
30
|
-
# Logic to determine if the table is a tenant table
|
31
|
-
# You can check if the table has a specific column or use a naming convention
|
32
|
-
PgRls.table_name.to_s == table_name
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'down_statements'
|
4
|
-
require_relative 'up_statements'
|
5
|
-
|
6
|
-
module PgRls
|
7
|
-
module Schema
|
8
|
-
# Schema Statements
|
9
|
-
module Statements
|
10
|
-
include UpStatements
|
11
|
-
include DownStatements
|
12
|
-
|
13
|
-
def create_rls_tenant_table(table_name, **, &)
|
14
|
-
create_rls_user
|
15
|
-
create_rls_setter_function
|
16
|
-
create_rls_blocking_function
|
17
|
-
create_table(table_name, **, &)
|
18
|
-
add_rls_column_to_tenant_table(table_name)
|
19
|
-
append_blocking_function(table_name)
|
20
|
-
end
|
21
|
-
|
22
|
-
def create_rls_table(table_name, **, &)
|
23
|
-
create_table(table_name, **, &)
|
24
|
-
add_rls_column(table_name)
|
25
|
-
create_rls_policy(table_name)
|
26
|
-
append_trigger_function(table_name)
|
27
|
-
end
|
28
|
-
|
29
|
-
def drop_rls_tenant_table(table_name)
|
30
|
-
drop_rls_setter_function
|
31
|
-
detach_blocking_function(table_name)
|
32
|
-
drop_table(table_name)
|
33
|
-
drop_rls_blocking_function
|
34
|
-
drop_rls_user
|
35
|
-
end
|
36
|
-
|
37
|
-
def drop_rls_table(table_name)
|
38
|
-
detach_trigger_function(table_name)
|
39
|
-
drop_rls_policy(table_name)
|
40
|
-
drop_table(table_name)
|
41
|
-
end
|
42
|
-
|
43
|
-
def convert_to_rls_tenant_table(table_name, **_options)
|
44
|
-
create_rls_user
|
45
|
-
create_rls_setter_function
|
46
|
-
create_rls_blocking_function
|
47
|
-
add_rls_column_to_tenant_table(table_name)
|
48
|
-
append_blocking_function(table_name)
|
49
|
-
end
|
50
|
-
|
51
|
-
def revert_rls_tenant_table(table_name)
|
52
|
-
drop_rls_setter_function
|
53
|
-
detach_blocking_function(table_name)
|
54
|
-
drop_rls_blocking_function
|
55
|
-
drop_rls_user
|
56
|
-
drop_rls_column(table_name)
|
57
|
-
end
|
58
|
-
|
59
|
-
def convert_to_rls_table(table_name)
|
60
|
-
add_rls_column(table_name)
|
61
|
-
create_rls_policy(table_name)
|
62
|
-
append_trigger_function(table_name)
|
63
|
-
end
|
64
|
-
|
65
|
-
def revert_rls_table(table_name)
|
66
|
-
detach_trigger_function(table_name)
|
67
|
-
drop_rls_policy(table_name)
|
68
|
-
drop_rls_column(table_name)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
module Schema
|
5
|
-
# Up Schema Statements
|
6
|
-
module UpStatements
|
7
|
-
def create_rls_user(name: PgRls.username, password: PgRls.password, schema: 'public')
|
8
|
-
ActiveRecord::Migration.execute <<-SQL
|
9
|
-
DO
|
10
|
-
$do$
|
11
|
-
BEGIN
|
12
|
-
IF NOT EXISTS (
|
13
|
-
SELECT FROM pg_catalog.pg_roles -- SELECT list can be empty for this
|
14
|
-
WHERE rolname = '#{name}') THEN
|
15
|
-
|
16
|
-
CREATE USER #{name} WITH PASSWORD '#{password}';
|
17
|
-
END IF;
|
18
|
-
GRANT ALL PRIVILEGES ON TABLE schema_migrations TO #{name};
|
19
|
-
GRANT USAGE ON SCHEMA #{schema} TO #{name};
|
20
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
21
|
-
GRANT USAGE, SELECT
|
22
|
-
ON SEQUENCES TO #{name};
|
23
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
24
|
-
GRANT SELECT, INSERT, UPDATE, DELETE
|
25
|
-
ON TABLES TO #{name};
|
26
|
-
GRANT SELECT, INSERT, UPDATE, DELETE
|
27
|
-
ON ALL TABLES IN SCHEMA #{schema}
|
28
|
-
TO #{name};
|
29
|
-
GRANT USAGE, SELECT
|
30
|
-
ON ALL SEQUENCES IN SCHEMA #{schema}
|
31
|
-
TO #{name};
|
32
|
-
END;
|
33
|
-
$do$;
|
34
|
-
SQL
|
35
|
-
end
|
36
|
-
|
37
|
-
def create_rls_blocking_function
|
38
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
39
|
-
CREATE OR REPLACE FUNCTION id_safe_guard ()
|
40
|
-
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
41
|
-
BEGIN
|
42
|
-
RAISE EXCEPTION 'This column is guarded due to tenancy dependency';
|
43
|
-
END $$;
|
44
|
-
SQL
|
45
|
-
end
|
46
|
-
|
47
|
-
def create_rls_setter_function
|
48
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
49
|
-
CREATE OR REPLACE FUNCTION tenant_id_setter ()
|
50
|
-
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
51
|
-
BEGIN
|
52
|
-
new.tenant_id:= (current_setting('rls.tenant_id'));
|
53
|
-
RETURN new;
|
54
|
-
END $$;
|
55
|
-
SQL
|
56
|
-
end
|
57
|
-
|
58
|
-
def append_blocking_function(table_name)
|
59
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
60
|
-
CREATE TRIGGER id_safe_guard
|
61
|
-
BEFORE UPDATE OF id ON #{table_name}
|
62
|
-
FOR EACH ROW EXECUTE PROCEDURE id_safe_guard();
|
63
|
-
SQL
|
64
|
-
end
|
65
|
-
|
66
|
-
def append_trigger_function(table_name)
|
67
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
68
|
-
CREATE TRIGGER tenant_id_setter
|
69
|
-
BEFORE INSERT OR UPDATE ON #{table_name}
|
70
|
-
FOR EACH ROW EXECUTE PROCEDURE tenant_id_setter();
|
71
|
-
SQL
|
72
|
-
end
|
73
|
-
|
74
|
-
def add_rls_column_to_tenant_table(table_name)
|
75
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
76
|
-
ALTER TABLE #{table_name}
|
77
|
-
ADD COLUMN IF NOT EXISTS
|
78
|
-
tenant_id uuid UNIQUE DEFAULT gen_random_uuid();
|
79
|
-
SQL
|
80
|
-
end
|
81
|
-
|
82
|
-
def add_rls_column(table_name)
|
83
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
84
|
-
ALTER TABLE #{table_name}
|
85
|
-
ADD COLUMN IF NOT EXISTS tenant_id uuid,
|
86
|
-
ADD CONSTRAINT fk_#{PgRls.table_name}
|
87
|
-
FOREIGN KEY (tenant_id)
|
88
|
-
REFERENCES #{PgRls.table_name}(tenant_id)
|
89
|
-
ON DELETE CASCADE;
|
90
|
-
SQL
|
91
|
-
end
|
92
|
-
|
93
|
-
def create_rls_policy(table_name, user = PgRls.username)
|
94
|
-
ActiveRecord::Migration.execute <<-SQL.squish
|
95
|
-
ALTER TABLE #{table_name} ENABLE ROW LEVEL SECURITY;
|
96
|
-
CREATE POLICY #{table_name}_#{user}
|
97
|
-
ON #{table_name}
|
98
|
-
TO #{user}
|
99
|
-
USING (tenant_id = NULLIF(current_setting('rls.tenant_id', TRUE), '')::uuid);
|
100
|
-
SQL
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
data/lib/pg_rls/tenant.rb
DELETED
@@ -1,153 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
# Tenant Controller
|
5
|
-
module Tenant
|
6
|
-
class << self
|
7
|
-
def switch(resource)
|
8
|
-
switch!(resource)
|
9
|
-
rescue PgRls::Errors::TenantNotFound
|
10
|
-
nil
|
11
|
-
end
|
12
|
-
|
13
|
-
def switch!(resource)
|
14
|
-
tenant = switch_tenant!(resource)
|
15
|
-
|
16
|
-
"RLS changed to '#{tenant.id}'"
|
17
|
-
rescue StandardError
|
18
|
-
Rails.logger.info('connection was not made')
|
19
|
-
raise PgRls::Errors::TenantNotFound
|
20
|
-
end
|
21
|
-
|
22
|
-
def with_tenant!(resource)
|
23
|
-
PgRls.main_model.connection_pool.with_connection do
|
24
|
-
tenant = switch_tenant!(resource)
|
25
|
-
|
26
|
-
yield(tenant).presence if block_given?
|
27
|
-
ensure
|
28
|
-
reset_rls! unless PgRls.test_inline_tenant == true || PgRls::Current::Context.tenant.blank?
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def fetch
|
33
|
-
fetch!
|
34
|
-
rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotFound
|
35
|
-
nil
|
36
|
-
end
|
37
|
-
|
38
|
-
def fetch!
|
39
|
-
PgRls::Current::Context.tenant ||= PgRls.main_model.connection_pool.with_connection do |connection|
|
40
|
-
tenant_id = get_tenant_id(connection)
|
41
|
-
if tenant_id.present?
|
42
|
-
PgRls.main_model.find_by!(
|
43
|
-
tenant_id:
|
44
|
-
)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# rubocop:disable Lint/RescueStandardError
|
50
|
-
# rubocop:disable Lint/UselessAssignment
|
51
|
-
def get_tenant_id(connection)
|
52
|
-
connection.execute("SELECT current_setting('rls.tenant_id')").getvalue(0, 0)
|
53
|
-
rescue => e
|
54
|
-
nil
|
55
|
-
end
|
56
|
-
# rubocop:enable Lint/RescueStandardError
|
57
|
-
# rubocop:enable Lint/UselessAssignment
|
58
|
-
|
59
|
-
def reset_rls!
|
60
|
-
PgRls.execute_rls_in_shards do |connection_class|
|
61
|
-
connection_class.connection_pool.with_connection do |connection|
|
62
|
-
connection.transaction do
|
63
|
-
connection.execute('RESET rls.tenant_id')
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
PgRls::Current::Context.clear_all
|
69
|
-
nil
|
70
|
-
end
|
71
|
-
|
72
|
-
def set_rls!(tenant)
|
73
|
-
tenant_id = tenant.tenant_id
|
74
|
-
PgRls.execute_rls_in_shards do |connection_class|
|
75
|
-
connection_class.connection_pool.with_connection do |connection|
|
76
|
-
connection.transaction do
|
77
|
-
connection.execute(format('SET rls.tenant_id = %s',
|
78
|
-
connection.quote(tenant_id)))
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
PgRls::Current::Context.clear_all
|
83
|
-
PgRls::Current::Context.tenant = tenant
|
84
|
-
end
|
85
|
-
|
86
|
-
def on_find_each(ids: [], scope: nil, &)
|
87
|
-
raise 'Invalid Scope' if scope.present? && PgRls.main_model != scope.klass
|
88
|
-
|
89
|
-
result = []
|
90
|
-
|
91
|
-
query = build_on_each_query(ids, scope)
|
92
|
-
|
93
|
-
query.find_each do |tenant|
|
94
|
-
result << { tenant_id: tenant.id, result: with_tenant!(tenant, &) }
|
95
|
-
end
|
96
|
-
|
97
|
-
result
|
98
|
-
end
|
99
|
-
|
100
|
-
private
|
101
|
-
|
102
|
-
def build_on_each_query(ids, scope)
|
103
|
-
return PgRls.main_model.all if ids.empty? && scope.blank?
|
104
|
-
|
105
|
-
return PgRls.main_model.where(id: ids) if scope.blank?
|
106
|
-
|
107
|
-
return scope.where(id: ids) if ids.present?
|
108
|
-
|
109
|
-
scope
|
110
|
-
end
|
111
|
-
|
112
|
-
def switch_tenant!(resource)
|
113
|
-
tenant = find_tenant(resource)
|
114
|
-
|
115
|
-
PgRls.establish_new_connection! if PgRls.admin_connection?
|
116
|
-
set_rls!(tenant)
|
117
|
-
|
118
|
-
tenant
|
119
|
-
rescue NoMethodError
|
120
|
-
raise PgRls::Errors::TenantNotFound
|
121
|
-
ensure
|
122
|
-
reset_rls! if tenant.blank?
|
123
|
-
end
|
124
|
-
|
125
|
-
def find_tenant(resource)
|
126
|
-
tenant = nil
|
127
|
-
|
128
|
-
PgRls.search_methods.each do |method|
|
129
|
-
break if tenant.present?
|
130
|
-
|
131
|
-
tenant = find_tenant_by_method(resource, method)
|
132
|
-
end
|
133
|
-
|
134
|
-
reset_rls! if reset_rls?(tenant)
|
135
|
-
raise PgRls::Errors::TenantNotFound if tenant.blank?
|
136
|
-
|
137
|
-
tenant
|
138
|
-
end
|
139
|
-
|
140
|
-
def reset_rls?(tenant)
|
141
|
-
PgRls::Current::Context.tenant.present? && tenant.present? && PgRls::Current::Context.tenant != tenant
|
142
|
-
end
|
143
|
-
|
144
|
-
def find_tenant_by_method(resource, method)
|
145
|
-
return resource if resource.is_a?(PgRls.main_model)
|
146
|
-
|
147
|
-
PgRls.main_model.unscoped.send(:"find_by_#{method}!", resource)
|
148
|
-
rescue ActiveRecord::RecordNotFound
|
149
|
-
nil
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|