pg_rls 0.0.2.6.11 → 0.1.0
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/.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
|