pg_rls 0.0.2.6.11 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +19 -158
- data/Gemfile +4 -1
- data/Gemfile.lock +192 -139
- data/README.md +3 -6
- data/lib/generators/pg_rls/active_record/active_record_generator.rb +10 -10
- data/lib/generators/pg_rls/active_record/templates/convert_migration_backport.rb.tt +1 -1
- data/lib/generators/pg_rls/install_generator.rb +10 -23
- data/lib/generators/templates/README +1 -3
- data/lib/generators/templates/pg_rls.rb.tt +13 -20
- data/lib/pg_rls/database/configurations.rb +33 -0
- data/lib/pg_rls/database/prepared.rb +1 -1
- data/lib/pg_rls/database/tasks/admin_database.rake +20 -43
- data/lib/pg_rls/errors/admin_username.rb +12 -0
- data/lib/pg_rls/errors/index.rb +5 -0
- data/lib/pg_rls/errors/rake_only_error.rb +12 -0
- data/lib/pg_rls/errors/tenant_not_found.rb +2 -10
- data/lib/pg_rls/middleware/set_reset_connection.rb +48 -3
- data/lib/pg_rls/middleware/sidekiq/client.rb +10 -1
- data/lib/pg_rls/middleware/sidekiq/server.rb +5 -1
- data/lib/pg_rls/multi_tenancy.rb +4 -4
- data/lib/pg_rls/schema/down_statements.rb +5 -5
- data/lib/pg_rls/schema/statements.rb +4 -4
- data/lib/pg_rls/schema/up_statements.rb +8 -8
- data/lib/pg_rls/tenant.rb +27 -26
- data/lib/pg_rls/version.rb +1 -1
- data/lib/pg_rls.rb +62 -69
- metadata +8 -8
- data/lib/pg_rls/schema/solo/statements.rb +0 -24
- data/lib/pg_rls/schema/solo/up_statements.rb +0 -106
- data/lib/pg_rls/secure_connection.rb +0 -23
- data/lib/pg_rls/solo/tenant.rb +0 -50
@@ -5,7 +5,7 @@ module PgRls
|
|
5
5
|
# Up Schema Statements
|
6
6
|
module UpStatements
|
7
7
|
def create_rls_user(name: PgRls.username, password: PgRls.password, schema: 'public')
|
8
|
-
|
8
|
+
ActiveRecord::Migration.execute <<-SQL
|
9
9
|
DO
|
10
10
|
$do$
|
11
11
|
BEGIN
|
@@ -35,7 +35,7 @@ module PgRls
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def create_rls_blocking_function
|
38
|
-
ActiveRecord::Migration.execute <<-SQL
|
38
|
+
ActiveRecord::Migration.execute <<-SQL.squish
|
39
39
|
CREATE OR REPLACE FUNCTION id_safe_guard ()
|
40
40
|
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
41
41
|
BEGIN
|
@@ -45,7 +45,7 @@ module PgRls
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def create_rls_setter_function
|
48
|
-
ActiveRecord::Migration.execute <<-SQL
|
48
|
+
ActiveRecord::Migration.execute <<-SQL.squish
|
49
49
|
CREATE OR REPLACE FUNCTION tenant_id_setter ()
|
50
50
|
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
51
51
|
BEGIN
|
@@ -56,7 +56,7 @@ module PgRls
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def append_blocking_function(table_name)
|
59
|
-
ActiveRecord::Migration.execute <<-SQL
|
59
|
+
ActiveRecord::Migration.execute <<-SQL.squish
|
60
60
|
CREATE TRIGGER id_safe_guard
|
61
61
|
BEFORE UPDATE OF id ON #{table_name}
|
62
62
|
FOR EACH ROW EXECUTE PROCEDURE id_safe_guard();
|
@@ -64,7 +64,7 @@ module PgRls
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def append_trigger_function(table_name)
|
67
|
-
ActiveRecord::Migration.execute <<-SQL
|
67
|
+
ActiveRecord::Migration.execute <<-SQL.squish
|
68
68
|
CREATE TRIGGER tenant_id_setter
|
69
69
|
BEFORE INSERT OR UPDATE ON #{table_name}
|
70
70
|
FOR EACH ROW EXECUTE PROCEDURE tenant_id_setter();
|
@@ -72,7 +72,7 @@ module PgRls
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def add_rls_column_to_tenant_table(table_name)
|
75
|
-
ActiveRecord::Migration.execute <<-SQL
|
75
|
+
ActiveRecord::Migration.execute <<-SQL.squish
|
76
76
|
ALTER TABLE #{table_name}
|
77
77
|
ADD COLUMN IF NOT EXISTS
|
78
78
|
tenant_id uuid UNIQUE DEFAULT gen_random_uuid();
|
@@ -80,7 +80,7 @@ module PgRls
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def add_rls_column(table_name)
|
83
|
-
ActiveRecord::Migration.execute <<-SQL
|
83
|
+
ActiveRecord::Migration.execute <<-SQL.squish
|
84
84
|
ALTER TABLE #{table_name}
|
85
85
|
ADD COLUMN IF NOT EXISTS tenant_id uuid,
|
86
86
|
ADD CONSTRAINT fk_#{PgRls.table_name}
|
@@ -91,7 +91,7 @@ module PgRls
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def create_rls_policy(table_name, user = PgRls.username)
|
94
|
-
ActiveRecord::Migration.execute <<-SQL
|
94
|
+
ActiveRecord::Migration.execute <<-SQL.squish
|
95
95
|
ALTER TABLE #{table_name} ENABLE ROW LEVEL SECURITY;
|
96
96
|
CREATE POLICY #{table_name}_#{user}
|
97
97
|
ON #{table_name}
|
data/lib/pg_rls/tenant.rb
CHANGED
@@ -25,16 +25,10 @@ module PgRls
|
|
25
25
|
raise e
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
PgRls.main_model.find_each do |tenant|
|
30
|
-
with_tenant!(tenant, &)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def with_tenant!(resource, &block)
|
28
|
+
def with_tenant!(resource)
|
35
29
|
tenant = switch_tenant!(resource)
|
36
30
|
|
37
|
-
|
31
|
+
yield(tenant) if block_given?
|
38
32
|
ensure
|
39
33
|
reset_rls! unless PgRls.test_inline_tenant == true
|
40
34
|
end
|
@@ -45,47 +39,50 @@ module PgRls
|
|
45
39
|
nil
|
46
40
|
end
|
47
41
|
|
48
|
-
def
|
42
|
+
def tenant!
|
49
43
|
@tenant ||= PgRls.main_model.find_by!(
|
50
44
|
tenant_id: PgRls.connection_class.connection.execute(
|
51
45
|
"SELECT current_setting('rls.tenant_id')"
|
52
46
|
).getvalue(0, 0)
|
53
47
|
)
|
54
48
|
end
|
55
|
-
|
56
|
-
def find_main_model
|
57
|
-
PgRls.main_model.ignored_columns = []
|
58
|
-
PgRls.main_model.find_by!(
|
59
|
-
tenant_id: PgRls.connection_class.connection.execute(
|
60
|
-
"SELECT current_setting('rls.tenant_id')"
|
61
|
-
).getvalue(0, 0)
|
62
|
-
)
|
63
|
-
end
|
49
|
+
alias fetch! tenant!
|
64
50
|
|
65
51
|
def reset_rls!
|
52
|
+
return if @tenant.blank?
|
53
|
+
|
66
54
|
@tenant = nil
|
67
|
-
PgRls.connection_class
|
55
|
+
PgRls.execute_rls_in_shards do |connection_class|
|
56
|
+
connection_class.transaction do
|
57
|
+
connection_class.connection.execute('RESET rls.tenant_id')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
nil
|
68
62
|
end
|
69
63
|
|
70
64
|
private
|
71
65
|
|
72
66
|
def switch_tenant!(resource)
|
67
|
+
# rubocop: disable Rails/IgnoredColumnsAssignment
|
73
68
|
PgRls.main_model.ignored_columns = []
|
69
|
+
# rubocop: enable Rails/IgnoredColumnsAssignment
|
74
70
|
|
75
|
-
connection_adapter = PgRls.connection_class
|
76
71
|
find_tenant(resource)
|
77
72
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
73
|
+
PgRls.execute_rls_in_shards do |connection_class|
|
74
|
+
connection_class.transaction do
|
75
|
+
connection_class.connection.execute(format('SET rls.tenant_id = %s',
|
76
|
+
connection_class.connection.quote(tenant.tenant_id)))
|
77
|
+
end
|
83
78
|
end
|
84
79
|
|
85
80
|
tenant
|
86
81
|
end
|
87
82
|
|
88
83
|
def find_tenant(resource)
|
84
|
+
raise PgRls::Errors::AdminUsername if admin_username?
|
85
|
+
|
89
86
|
reset_rls!
|
90
87
|
|
91
88
|
PgRls.search_methods.each do |method|
|
@@ -95,7 +92,11 @@ module PgRls
|
|
95
92
|
@tenant = find_tenant_by_method(resource, method)
|
96
93
|
end
|
97
94
|
|
98
|
-
raise PgRls::Errors::TenantNotFound if tenant.
|
95
|
+
raise PgRls::Errors::TenantNotFound if tenant.blank?
|
96
|
+
end
|
97
|
+
|
98
|
+
def admin_username?
|
99
|
+
PgRls.username != PgRls.current_db_username
|
99
100
|
end
|
100
101
|
|
101
102
|
def find_tenant_by_method(resource, method)
|
data/lib/pg_rls/version.rb
CHANGED
data/lib/pg_rls.rb
CHANGED
@@ -5,13 +5,11 @@ require 'forwardable'
|
|
5
5
|
require_relative 'pg_rls/version'
|
6
6
|
require_relative 'pg_rls/database/prepared'
|
7
7
|
require_relative 'pg_rls/schema/statements'
|
8
|
-
require_relative 'pg_rls/
|
9
|
-
require_relative 'pg_rls/solo/tenant'
|
8
|
+
require_relative 'pg_rls/database/configurations'
|
10
9
|
require_relative 'pg_rls/tenant'
|
11
|
-
require_relative 'pg_rls/secure_connection'
|
12
10
|
require_relative 'pg_rls/multi_tenancy'
|
13
11
|
require_relative 'pg_rls/railtie' if defined?(Rails)
|
14
|
-
require_relative 'pg_rls/errors/
|
12
|
+
require_relative 'pg_rls/errors/index'
|
15
13
|
|
16
14
|
# PostgreSQL Row Level Security
|
17
15
|
module PgRls
|
@@ -21,13 +19,13 @@ module PgRls
|
|
21
19
|
class << self
|
22
20
|
extend Forwardable
|
23
21
|
|
24
|
-
WRITER_METHODS = %i[table_name class_name search_methods
|
22
|
+
WRITER_METHODS = %i[table_name class_name search_methods].freeze
|
25
23
|
READER_METHODS = %i[
|
26
|
-
connection_class
|
24
|
+
connection_class execute table_name class_name search_methods
|
27
25
|
].freeze
|
28
26
|
DELEGATORS_METHODS = %i[
|
29
|
-
connection_class
|
30
|
-
class_name
|
27
|
+
connection_class execute table_name search_methods
|
28
|
+
class_name main_model
|
31
29
|
].freeze
|
32
30
|
|
33
31
|
attr_writer(*WRITER_METHODS)
|
@@ -36,100 +34,104 @@ module PgRls
|
|
36
34
|
def_delegators(*DELEGATORS_METHODS)
|
37
35
|
|
38
36
|
def setup
|
37
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.include PgRls::Schema::Statements
|
38
|
+
ActiveRecord::Base.ignored_columns += %w[tenant_id]
|
39
|
+
|
39
40
|
yield self
|
40
41
|
end
|
41
42
|
|
42
|
-
def
|
43
|
-
|
43
|
+
def connection_class
|
44
|
+
@connection_class ||= ActiveRecord::Base
|
44
45
|
end
|
45
46
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
def rake_tasks?
|
48
|
+
Rake.application.top_level_tasks.present? || ARGV.any? { |arg| arg =~ /rake|dsl/ }
|
49
|
+
rescue NoMethodError
|
50
|
+
false
|
50
51
|
end
|
51
52
|
|
52
|
-
def
|
53
|
-
|
54
|
-
end
|
53
|
+
def admin_tasks_execute
|
54
|
+
raise PgRls::Errors::RakeOnlyError unless rake_tasks?
|
55
55
|
|
56
|
-
|
57
|
-
ActiveRecord::Base.connection.disconnect! if ActiveRecord::Base.connection_pool.connected?
|
56
|
+
self.as_db_admin = true
|
58
57
|
|
59
|
-
|
58
|
+
yield
|
59
|
+
ensure
|
60
|
+
self.as_db_admin = false
|
60
61
|
end
|
61
62
|
|
62
|
-
def admin_execute(query = nil)
|
63
|
+
def admin_execute(query = nil, &)
|
64
|
+
current_tenant = PgRls::Tenant.fetch
|
65
|
+
|
63
66
|
self.as_db_admin = true
|
64
|
-
establish_new_connection
|
65
|
-
|
67
|
+
establish_new_connection!
|
68
|
+
|
69
|
+
return ensure_block_execution(&) if block_given?
|
66
70
|
|
67
71
|
execute(query)
|
68
72
|
ensure
|
69
73
|
self.as_db_admin = false
|
70
|
-
establish_new_connection
|
71
|
-
|
72
|
-
|
73
|
-
def establish_default_connection=(value)
|
74
|
-
ENV['AS_DB_ADMIN'] = value.to_s
|
75
|
-
@default_connection = value
|
74
|
+
establish_new_connection!
|
75
|
+
PgRls::Tenant.switch(current_tenant) if current_tenant.present?
|
76
76
|
end
|
77
77
|
|
78
|
-
def
|
79
|
-
|
78
|
+
def establish_new_connection!
|
79
|
+
execute_rls_in_shards do |connection_class, pool|
|
80
|
+
connection_class.remove_connection
|
81
|
+
connection_class.establish_connection(pool.db_config)
|
82
|
+
end
|
80
83
|
end
|
81
84
|
|
82
85
|
def main_model
|
83
86
|
class_name.to_s.camelize.constantize
|
84
87
|
end
|
85
88
|
|
86
|
-
def
|
87
|
-
|
89
|
+
def on_each_tenant(&)
|
90
|
+
result = []
|
91
|
+
main_model.find_each do |tenant|
|
88
92
|
allowed_search_fields = search_methods.map(&:to_s).intersection(main_model.column_names)
|
89
93
|
Tenant.switch tenant.send(allowed_search_fields.first)
|
90
94
|
|
91
|
-
|
95
|
+
result << { tenant:, result: ensure_block_execution(tenant, &) }
|
92
96
|
end
|
93
|
-
end
|
94
97
|
|
95
|
-
|
96
|
-
connection_class.connection_db_config.configuration_hash[:username]
|
97
|
-
end
|
98
|
+
PgRls::Tenant.reset_rls!
|
98
99
|
|
99
|
-
|
100
|
-
ActiveRecord::Migration.execute(query)
|
100
|
+
result
|
101
101
|
end
|
102
102
|
|
103
|
-
def
|
104
|
-
connection_class.
|
105
|
-
|
106
|
-
connection_class.connection_db_config.configuration_hash
|
107
|
-
end
|
103
|
+
def execute_rls_in_shards
|
104
|
+
connection_pool_list = PgRls.connection_class.connection_handler.connection_pool_list
|
105
|
+
result = []
|
108
106
|
|
109
|
-
|
110
|
-
|
107
|
+
connection_pool_list.each do |pool|
|
108
|
+
pool.connection.transaction do
|
109
|
+
Rails.logger.info("Executing in #{pool.connection.connection_class}")
|
111
110
|
|
112
|
-
|
111
|
+
result << yield(pool.connection.connection_class, pool)
|
112
|
+
end
|
113
|
+
end
|
113
114
|
|
114
|
-
|
115
|
+
result
|
115
116
|
end
|
116
117
|
|
117
|
-
def
|
118
|
-
|
118
|
+
def current_db_username
|
119
|
+
ActiveRecord::Base.connection_db_config.configuration_hash[:username]
|
120
|
+
end
|
119
121
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
122
|
+
def as_db_admin?
|
123
|
+
@as_db_admin || false
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
125
127
|
|
126
|
-
|
128
|
+
attr_writer :as_db_admin
|
129
|
+
|
130
|
+
def ensure_block_execution(*, **)
|
131
|
+
yield(*, **).presence
|
127
132
|
end
|
128
133
|
end
|
129
134
|
|
130
|
-
mattr_accessor :as_db_admin
|
131
|
-
@@as_db_admin = false
|
132
|
-
|
133
135
|
mattr_accessor :table_name
|
134
136
|
@@table_name = 'companies'
|
135
137
|
|
@@ -142,18 +144,9 @@ module PgRls
|
|
142
144
|
mattr_accessor :password
|
143
145
|
@@password = 'password'
|
144
146
|
|
145
|
-
mattr_accessor :solo_mode
|
146
|
-
@@solo_mode = false
|
147
|
-
|
148
147
|
mattr_accessor :test_inline_tenant
|
149
148
|
@@test_inline_tenant = false
|
150
149
|
|
151
|
-
mattr_accessor :session_key
|
152
|
-
@@session_key = '_hub_sessions'
|
153
|
-
|
154
|
-
mattr_accessor :session_prefix
|
155
|
-
@@session_prefix = '_session_id:2::'
|
156
|
-
|
157
150
|
mattr_accessor :search_methods
|
158
151
|
@@search_methods = %i[subdomain id tenant_id]
|
159
152
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_rls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Laloush
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -61,8 +61,12 @@ files:
|
|
61
61
|
- lib/generators/templates/pg_rls.rb.tt
|
62
62
|
- lib/pg_rls.rb
|
63
63
|
- lib/pg_rls/Rakefile
|
64
|
+
- lib/pg_rls/database/configurations.rb
|
64
65
|
- lib/pg_rls/database/prepared.rb
|
65
66
|
- lib/pg_rls/database/tasks/admin_database.rake
|
67
|
+
- lib/pg_rls/errors/admin_username.rb
|
68
|
+
- lib/pg_rls/errors/index.rb
|
69
|
+
- lib/pg_rls/errors/rake_only_error.rb
|
66
70
|
- lib/pg_rls/errors/tenant_not_found.rb
|
67
71
|
- lib/pg_rls/middleware.rb
|
68
72
|
- lib/pg_rls/middleware/set_reset_connection.rb
|
@@ -72,12 +76,8 @@ files:
|
|
72
76
|
- lib/pg_rls/multi_tenancy.rb
|
73
77
|
- lib/pg_rls/railtie.rb
|
74
78
|
- lib/pg_rls/schema/down_statements.rb
|
75
|
-
- lib/pg_rls/schema/solo/statements.rb
|
76
|
-
- lib/pg_rls/schema/solo/up_statements.rb
|
77
79
|
- lib/pg_rls/schema/statements.rb
|
78
80
|
- lib/pg_rls/schema/up_statements.rb
|
79
|
-
- lib/pg_rls/secure_connection.rb
|
80
|
-
- lib/pg_rls/solo/tenant.rb
|
81
81
|
- lib/pg_rls/tenant.rb
|
82
82
|
- lib/pg_rls/version.rb
|
83
83
|
homepage: https://github.com/Dandush03/pg_rls
|
@@ -93,14 +93,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '3.
|
96
|
+
version: '3.2'
|
97
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
98
|
requirements:
|
99
99
|
- - ">="
|
100
100
|
- !ruby/object:Gem::Version
|
101
101
|
version: '0'
|
102
102
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
103
|
+
rubygems_version: 3.4.19
|
104
104
|
signing_key:
|
105
105
|
specification_version: 4
|
106
106
|
summary: Write a short summary, because RubyGems requires one.
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../statements'
|
4
|
-
require_relative './up_statements'
|
5
|
-
|
6
|
-
module PgRls
|
7
|
-
module Schema
|
8
|
-
module Solo
|
9
|
-
# Schema Solo Statements
|
10
|
-
module Statements
|
11
|
-
include PgRls::Schema::Statements
|
12
|
-
include PgRls::Schema::Solo::UpStatements
|
13
|
-
|
14
|
-
def create_rls_table(table_name, **options, &)
|
15
|
-
setup_rls_tenant_table
|
16
|
-
create_table(table_name, **options, &)
|
17
|
-
add_rls_column(table_name)
|
18
|
-
create_rls_policy(table_name)
|
19
|
-
append_trigger_function(table_name)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
module Schema
|
5
|
-
module Solo
|
6
|
-
# Up Schema Solo Statements
|
7
|
-
module UpStatements
|
8
|
-
def setup_rls_tenant_table
|
9
|
-
ActiveRecord::Migration.execute <<-SQL
|
10
|
-
DO
|
11
|
-
$do$
|
12
|
-
BEGIN
|
13
|
-
IF NOT EXISTS (
|
14
|
-
SELECT FROM pg_tables
|
15
|
-
WHERE schemaname = 'public' AND tablename = '#{PgRls.table_name}') THEN
|
16
|
-
#{create_rls_user}
|
17
|
-
#{create_rls_setter_function}
|
18
|
-
#{create_rls_blocking_function}
|
19
|
-
#{create_rls_solo_tenant_table}
|
20
|
-
#{append_blocking_function}
|
21
|
-
END IF;
|
22
|
-
END;
|
23
|
-
$do$;
|
24
|
-
SQL
|
25
|
-
end
|
26
|
-
|
27
|
-
def create_rls_user(name: PgRls.username, password: PgRls.password, schema: 'public')
|
28
|
-
<<~SQL
|
29
|
-
-- Grant Role Permissions
|
30
|
-
BEGIN
|
31
|
-
IF NOT EXISTS (
|
32
|
-
SELECT FROM pg_catalog.pg_roles -- SELECT list can be empty for this
|
33
|
-
WHERE rolname = '#{name}') THEN
|
34
|
-
|
35
|
-
CREATE USER #{name} WITH PASSWORD '#{password}';
|
36
|
-
END IF;
|
37
|
-
GRANT ALL PRIVILEGES ON TABLE schema_migrations TO #{name};
|
38
|
-
GRANT USAGE ON SCHEMA #{schema} TO #{name};
|
39
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
40
|
-
GRANT USAGE, SELECT
|
41
|
-
ON SEQUENCES TO #{name};
|
42
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
43
|
-
GRANT SELECT, INSERT, UPDATE, DELETE
|
44
|
-
ON TABLES TO #{name};
|
45
|
-
GRANT SELECT, INSERT, UPDATE, DELETE
|
46
|
-
ON ALL TABLES IN SCHEMA #{schema}
|
47
|
-
TO #{name};
|
48
|
-
GRANT USAGE, SELECT
|
49
|
-
ON ALL SEQUENCES IN SCHEMA #{schema}
|
50
|
-
TO #{name};
|
51
|
-
END;
|
52
|
-
SQL
|
53
|
-
end
|
54
|
-
|
55
|
-
def create_rls_setter_function
|
56
|
-
<<~SQL
|
57
|
-
-- Create RLS Setter Function
|
58
|
-
CREATE OR REPLACE FUNCTION tenant_id_setter ()
|
59
|
-
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
60
|
-
BEGIN
|
61
|
-
IF NOT EXISTS (
|
62
|
-
SELECT FROM #{PgRls.table_name}
|
63
|
-
WHERE tenant_id = (current_setting('rls.tenant_id'))::uuid
|
64
|
-
) THEN
|
65
|
-
INSERT INTO #{PgRls.table_name} (tenant_id)
|
66
|
-
VALUES ((current_setting('rls.tenant_id'))::uuid);
|
67
|
-
END IF;
|
68
|
-
|
69
|
-
NEW.tenant_id:= (current_setting('rls.tenant_id'));
|
70
|
-
RETURN NEW;
|
71
|
-
END $$;
|
72
|
-
SQL
|
73
|
-
end
|
74
|
-
|
75
|
-
def create_rls_blocking_function
|
76
|
-
<<~SQL
|
77
|
-
-- Create RLS Blocking Function
|
78
|
-
CREATE OR REPLACE FUNCTION id_safe_guard ()
|
79
|
-
RETURNS TRIGGER LANGUAGE plpgsql AS $$
|
80
|
-
BEGIN
|
81
|
-
RAISE EXCEPTION 'This column is guarded due to tenancy dependency';
|
82
|
-
END $$;
|
83
|
-
SQL
|
84
|
-
end
|
85
|
-
|
86
|
-
def create_rls_solo_tenant_table
|
87
|
-
<<~SQL
|
88
|
-
-- Create Tenant Table
|
89
|
-
CREATE TABLE #{PgRls.table_name} (
|
90
|
-
tenant_id uuid PRIMARY KEY
|
91
|
-
);
|
92
|
-
SQL
|
93
|
-
end
|
94
|
-
|
95
|
-
def append_blocking_function
|
96
|
-
<<~SQL
|
97
|
-
-- Append Blocking Function
|
98
|
-
CREATE TRIGGER id_safe_guard
|
99
|
-
BEFORE UPDATE OF tenant_id ON #{PgRls.table_name}
|
100
|
-
FOR EACH ROW EXECUTE PROCEDURE id_safe_guard();
|
101
|
-
SQL
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
# Ensure Connection is with App_use
|
5
|
-
module SecureConnection
|
6
|
-
def self.establish_secure_connection
|
7
|
-
return if secure_connection_established?
|
8
|
-
|
9
|
-
return if PgRls.default_connection?
|
10
|
-
|
11
|
-
PgRls.establish_new_connection
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.secure_connection_established?
|
15
|
-
PgRls.current_connection_username == PgRls.username
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.included(base)
|
19
|
-
establish_secure_connection
|
20
|
-
base.ignored_columns = %w[tenant_id]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/pg_rls/solo/tenant.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
module Solo
|
5
|
-
# Set and Fetch Tenant without loading a model
|
6
|
-
class Tenant
|
7
|
-
class << self
|
8
|
-
attr_reader :tenant
|
9
|
-
|
10
|
-
def switch!(resource)
|
11
|
-
switch_tenant!(resource)
|
12
|
-
rescue StandardError => e
|
13
|
-
Rails.logger.info('connection was not made')
|
14
|
-
raise e
|
15
|
-
end
|
16
|
-
|
17
|
-
def fetch
|
18
|
-
@fetch ||= PgRls.connection_class.connection.execute(
|
19
|
-
"SELECT current_setting('rls.tenant_id')"
|
20
|
-
).getvalue(0, 0)
|
21
|
-
end
|
22
|
-
|
23
|
-
def around(resource)
|
24
|
-
switch_tenant!(resource)
|
25
|
-
yield
|
26
|
-
ensure
|
27
|
-
reset_rls!
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def reset_rls!
|
33
|
-
@fetch = nil
|
34
|
-
@tenant = nil
|
35
|
-
PgRls.connection_class.connection.execute('RESET rls.tenant_id')
|
36
|
-
end
|
37
|
-
|
38
|
-
def switch_tenant!(resource)
|
39
|
-
connection_adapter = PgRls.connection_class
|
40
|
-
|
41
|
-
raise PgRls::Errors::TenantNotFound if resource.blank?
|
42
|
-
|
43
|
-
connection_adapter.connection.execute(format('SET rls.tenant_id = %s',
|
44
|
-
connection_adapter.connection.quote(resource)))
|
45
|
-
"RLS changed to '#{resource}'"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|