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.
@@ -20,11 +20,11 @@ module PgRls
20
20
  end
21
21
 
22
22
  def create_tenant_migration_file
23
- if creating?
24
- migration_template(create_migration_template_path,
25
- "#{migration_path}/#{create_file_sub_name}_#{table_name}.rb",
26
- migration_version:)
27
- end
23
+ return unless creating?
24
+
25
+ migration_template(create_migration_template_path,
26
+ "#{migration_path}/#{create_file_sub_name}_#{table_name}.rb",
27
+ migration_version:)
28
28
  end
29
29
 
30
30
  def convert_tenant_migration_file
@@ -34,11 +34,11 @@ module PgRls
34
34
  migration_version:)
35
35
  end
36
36
 
37
- if installation_in_progress?
38
- migration_template('convert_migration_backport.rb.tt',
39
- "#{migration_path}/pg_rls_backport_#{table_name}.rb",
40
- migration_version:)
41
- end
37
+ return unless installation_in_progress?
38
+
39
+ migration_template('convert_migration_backport.rb.tt',
40
+ "#{migration_path}/pg_rls_backport_#{table_name}.rb",
41
+ migration_version:)
42
42
  end
43
43
 
44
44
  def create_model_file
@@ -3,7 +3,7 @@
3
3
  class PgRlsBackport<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
4
  def up
5
5
  # Suggested Code:
6
- # PgRls.all_tenants do |tenant|
6
+ # PgRls.on_each_tenant do |tenant|
7
7
  # tenant.<%= table_name %>.in_batches(of: 100) do |<%= table_name %>|
8
8
  # <%= table_name %>.each { |<%= table_name.singularize %>| <%= table_name.singularize %>.update_attribute('tenant_id', tenant.tenant_id) }
9
9
  # end
@@ -32,13 +32,13 @@ module PgRls
32
32
  hook_for :orm, required: true
33
33
 
34
34
  def orm_error_message
35
- <<-ERROR.strip_heredoc
36
- An ORM must be set to install PgRls in your application.
37
- Be sure to have an ORM like Active Record or loaded in your
38
- app or configure your own at `config/application.rb`.
39
- config.generators do |g|
40
- g.orm :your_orm_gem
41
- end
35
+ <<~ERROR
36
+ An ORM must be set to install PgRls in your application.
37
+ Be sure to have an ORM like Active Record or loaded in your
38
+ app or configure your own at `config/application.rb`.
39
+ config.generators do |g|
40
+ g.orm :your_orm_gem
41
+ end
42
42
  ERROR
43
43
  end
44
44
 
@@ -46,7 +46,6 @@ module PgRls
46
46
  raise MissingORMError, orm_error_message unless options[:orm]
47
47
 
48
48
  inject_include_to_application
49
- inject_include_to_application_record
50
49
  inject_include_to_application_controller
51
50
  template 'pg_rls.rb.tt', 'config/initializers/pg_rls.rb'
52
51
  end
@@ -54,23 +53,15 @@ module PgRls
54
53
  def inject_include_to_application
55
54
  return if aplication_already_included?
56
55
 
57
- gsub_file(APPLICATION_PATH, /(#{Regexp.escape(APPLICATION_LINE)})/mi) do |match|
56
+ gsub_file(APPLICATION_PATH, /(#{Regexp.escape(APPLICATION_LINE)})/mio) do |match|
58
57
  "#{match}\n config.active_record.schema_format = :sql\n"
59
58
  end
60
59
  end
61
60
 
62
- def inject_include_to_application_record
63
- return if aplication_record_already_included?
64
-
65
- gsub_file(APPLICATION_RECORD_PATH, /(#{Regexp.escape(APPLICATION_RECORD_LINE)})/mi) do |match|
66
- "#{match}\n include PgRls::SecureConnection\n"
67
- end
68
- end
69
-
70
61
  def inject_include_to_application_controller
71
62
  return if aplication_controller_already_included?
72
63
 
73
- gsub_file(APPLICATION_CONTROLLER_PATH, /(#{Regexp.escape(APPLICATION_CONTROLLER_LINE)})/mi) do |match|
64
+ gsub_file(APPLICATION_CONTROLLER_PATH, /(#{Regexp.escape(APPLICATION_CONTROLLER_LINE)})/mio) do |match|
74
65
  "#{match}\n include PgRls::MultiTenancy\n"
75
66
  end
76
67
  end
@@ -79,16 +70,12 @@ module PgRls
79
70
  File.readlines(APPLICATION_CONTROLLER_PATH).grep(/include PgRls::MultiTenancy/).any?
80
71
  end
81
72
 
82
- def aplication_record_already_included?
83
- File.readlines(APPLICATION_RECORD_PATH).grep(/include PgRls::SecureConnection/).any?
84
- end
85
-
86
73
  def aplication_already_included?
87
74
  File.readlines(APPLICATION_PATH).grep(/config.active_record.schema_format = :sql/).any?
88
75
  end
89
76
 
90
77
  def initialize_error_text
91
- <<-ERROR.strip_heredoc
78
+ <<~ERROR
92
79
  TO DO
93
80
  ERROR
94
81
  end
@@ -2,12 +2,10 @@ README
2
2
  ===============================================================================
3
3
  WARNING!!
4
4
 
5
- PgRls required that ActiveRecord format migration as SQL
5
+ PgRls required that ActiveRecord format migration as SQL
6
6
 
7
7
  Once you remove a tenant all of his data would be removed as well
8
8
 
9
- PgRls::SecureConnection was included to your ApplicationRecord do not remove it
10
-
11
9
  If you're setting a custom user, make sure to regenerate the structure.sql since
12
10
  Postgresql policies are created on each user
13
11
 
@@ -20,25 +20,18 @@ PgRls.setup do |config|
20
20
  # config.username = Rails.application.credentials.dig(:database, :username)
21
21
  # config.password = Rails.application.credentials.dig(:database, :password)
22
22
 
23
+ ## ------------------------------ Middleware SetResetConnection -----------------------------
24
+ ## Uncomment this lines if you're using SetResetConnection Middleware
25
+ #
26
+ # config.session_store_server = Rails.application.config_for(:redis).session
27
+ #
28
+ ## Uncomment this line if you're not using warden as your authentication system or if you
29
+ ## changed the default warden key. Devise, uses warden authentication.
30
+ # config.session_store_default_warden_key = '2'
31
+ #
32
+ ## Uncomment this line if you're setting a diferent session key than stablished under your
33
+ ## redis server configuration
34
+ # config.session_key_prefix = '_hub_session'
23
35
  ##
24
- ## Uncomment this lines in order to enable solo mode
25
- ## Solo mode is made for API mode where we don't want to repeate the same
26
- ## data structure across many project, Solo mode create a hidden tenant table
27
- ## which is autopopulated on each request
28
- # config.solo_mode = true
29
-
30
- ##
31
- ## After installing the PgRls gem, you can add the `PgRls::Middleware::SetResetConnection`
32
- ## middleware to your Rails application to secure all database connections using Row Level Security.
33
- ## To add the middleware using the `middleware.use` method, add the following
34
- ## lines to your `config/application.rb` file:
35
- ## require 'pg_rls/middleware/set_reset_connection'
36
- ## config.middleware.use PgRls::Middleware::SetResetConnection
37
- ## Note: Be sure to add the `PgRls::Middleware::SetResetConnection` middleware after any
38
- ## middleware that sets up the Rails session, since the RLS middleware depends
39
- ## on the presence of the session to work correctly.
40
- ## Additionally, you will need to manually require the `PgRls::Middleware::SetResetConnection`
41
- ## file in your application, as shown in the first line of the code snippet above.
42
- # config.session_key = '_hub_sessions'
43
- # config.session_prefix = '_session_id:2::'
36
+ ## ------------------------------ Middleware SetResetConnection -----------------------------
44
37
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record/database_configurations'
4
+
5
+ module ActiveRecord
6
+ # ActiveRecord::DatabaseConfigurations
7
+ class DatabaseConfigurations
8
+ class HashConfig
9
+ def initialize(env_name, name, configuration_hash)
10
+ @env_name = env_name
11
+ @name = name
12
+ @configuration_hash = configuration_hash
13
+ end
14
+
15
+ def configuration_hash
16
+ return admin_configuration_hash if PgRls.as_db_admin?
17
+
18
+ rls_configuration_hash
19
+ end
20
+
21
+ def admin_configuration_hash
22
+ @admin_configuration_hash ||= @configuration_hash
23
+ end
24
+
25
+ def rls_configuration_hash
26
+ @rls_configuration_hash ||= @configuration_hash.deep_dup.tap do |config|
27
+ config[:username] = PgRls.username
28
+ config[:password] = PgRls.password
29
+ end.freeze
30
+ end
31
+ end
32
+ end
33
+ end
@@ -6,7 +6,7 @@ module PgRls
6
6
  module Prepared
7
7
  class << self
8
8
  def grant_user_credentials(name: PgRls.username, schema: 'public')
9
- PgRls.admin_execute <<-SQL
9
+ PgRls.admin_execute <<-SQL.squish
10
10
  DO
11
11
  $do$
12
12
  BEGIN
@@ -22,128 +22,105 @@ end
22
22
  namespace :db do
23
23
  include PgRls::Schema::UpStatements
24
24
 
25
- def admin_connection
26
- PgRls.as_db_admin = true
27
- PgRls.establish_new_connection
28
-
29
- yield
30
- ensure
31
- PgRls.as_db_admin = false
32
- ActiveRecord::Base.connection.disconnect!
33
- PgRls.establish_new_connection
34
- end
35
-
36
25
  override_task grant_usage: :load_config do
37
- admin_connection do
26
+ PgRls.admin_tasks_execute do
38
27
  create_rls_user
39
28
  end
40
29
  end
41
30
 
42
31
  override_task create: :load_config do
43
- admin_connection do
32
+ PgRls.admin_tasks_execute do
44
33
  Rake::Task['db:create:original'].invoke
45
34
  end
46
35
  end
47
36
 
48
37
  override_task drop: :load_config do
49
- admin_connection do
38
+ PgRls.admin_tasks_execute do
50
39
  Rake::Task['db:drop:original'].invoke
51
40
  end
52
41
  end
53
42
 
54
43
  override_task migrate: :load_config do
55
- admin_connection do
44
+ PgRls.admin_tasks_execute do
56
45
  Rake::Task['db:migrate:original'].invoke
57
46
  end
58
47
  end
59
48
 
60
49
  override_task rollback: :load_config do
61
- admin_connection do
50
+ PgRls.admin_tasks_execute do
62
51
  Rake::Task['db:rollback:original'].invoke
63
52
  end
64
53
  end
65
54
 
66
55
  override_task prepare: :load_config do
67
- admin_connection do
56
+ PgRls.admin_tasks_execute do
68
57
  Rake::Task['db:prepare:original'].invoke
69
58
  end
70
59
  end
71
60
 
72
61
  override_task setup: :load_config do
73
- admin_connection do
62
+ PgRls.admin_tasks_execute do
74
63
  Rake::Task['db:setup:original'].invoke
75
64
  end
76
65
  end
77
66
 
78
67
  override_task prepare: :load_config do
79
- admin_connection do
68
+ PgRls.admin_tasks_execute do
80
69
  Rake::Task['db:reset:original'].invoke
81
70
  end
82
71
  end
83
72
 
84
73
  override_task purge: :load_config do
85
- admin_connection do
74
+ PgRls.admin_tasks_execute do
86
75
  Rake::Task['db:purge:original'].invoke
87
76
  end
88
77
  end
89
78
 
90
79
  override_task abort_if_pending_migrations: :load_config do
91
- admin_connection do
80
+ PgRls.admin_tasks_execute do
92
81
  Rake::Task['db:abort_if_pending_migrations:original'].invoke
93
82
  end
94
83
  end
95
84
 
96
85
  namespace :test do
97
- def admin_connection_test_db
98
- Rails.env = 'test'
99
- PgRls.as_db_admin = true
100
- PgRls.establish_new_connection
101
-
102
- yield
103
- ensure
104
- PgRls.as_db_admin = false
105
- ActiveRecord::Base.connection.disconnect!
106
- PgRls.establish_new_connection
107
- end
108
-
109
86
  override_task grant_usage: :load_config do
110
- admin_connection_test_db do
87
+ PgRls.admin_tasks_execute do
111
88
  create_rls_user
112
89
  end
113
90
  end
114
91
 
115
92
  override_task create: :load_config do
116
- admin_connection_test_db do
93
+ PgRls.admin_tasks_execute do
117
94
  Rake::Task['db:test:create:original'].invoke
118
95
  end
119
96
  end
120
97
 
121
98
  override_task drop: :load_config do
122
- admin_connection_test_db do
99
+ PgRls.admin_tasks_execute do
123
100
  Rake::Task['db:test:drop:original'].invoke
124
101
  end
125
102
  end
126
103
 
127
104
  override_task prepare: :load_config do
128
- admin_connection_test_db do
105
+ PgRls.admin_tasks_execute do
129
106
  Rake::Task['db:test:prepare:original'].invoke
130
107
  end
131
108
  end
132
109
 
133
110
  override_task setup: :load_config do
134
- admin_connection_test_db do
111
+ PgRls.admin_tasks_execute do
135
112
  Rake::Task['db:test:setup:original'].invoke
136
113
  end
137
114
  end
138
115
 
139
116
  override_task purge: :load_config do
140
- admin_connection_test_db do
117
+ PgRls.admin_tasks_execute do
141
118
  Rake::Task['db:test:purge:original'].invoke
142
119
  end
143
120
  end
144
121
 
145
122
  override_task load_schema: :load_config do
146
- admin_connection_test_db do
123
+ PgRls.admin_tasks_execute do
147
124
  Rake::Task['db:test:load_schema:original'].invoke
148
125
  end
149
126
  end
@@ -151,7 +128,7 @@ namespace :db do
151
128
 
152
129
  namespace :environment do
153
130
  override_task set: :load_config do
154
- admin_connection do
131
+ PgRls.admin_tasks_execute do
155
132
  Rake::Task['db:environment:set:original'].invoke
156
133
  end
157
134
  end
@@ -159,7 +136,7 @@ namespace :db do
159
136
 
160
137
  namespace :schema do
161
138
  override_task load: :load_config do
162
- admin_connection do
139
+ PgRls.admin_tasks_execute do
163
140
  Rake::Task['db:schema:load:original'].invoke
164
141
  Rake::Task['db:grant_usage'].invoke
165
142
  Rake::Task['db:test:grant_usage'].invoke
@@ -167,7 +144,7 @@ namespace :db do
167
144
  end
168
145
 
169
146
  override_task dump: :load_config do
170
- admin_connection do
147
+ PgRls.admin_tasks_execute do
171
148
  Rake::Task['db:schema:dump:original'].invoke
172
149
  end
173
150
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRls
4
+ module Errors
5
+ class AdminUsername < StandardError
6
+ def initialize(msg = nil)
7
+ msg ||= 'Cannot set or reset tenant for admin user'
8
+ super(msg)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rake_only_error'
4
+ require_relative 'tenant_not_found'
5
+ require_relative 'admin_username'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRls
4
+ module Errors
5
+ class RakeOnlyError < StandardError
6
+ def initialize(msg = nil)
7
+ msg ||= 'This method can only be executed through rake tasks'
8
+ super(msg)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -5,18 +5,10 @@ module PgRls
5
5
  # Raise Tenant Not found and ensure that the tenant is resetted
6
6
  class TenantNotFound < StandardError
7
7
  def initialize(msg = nil)
8
- reset_tenant_id
9
- @msg = msg
8
+ PgRls::Tenant.reset_rls!
9
+ msg ||= "Tenant Doesn't exist"
10
10
  super(msg)
11
11
  end
12
-
13
- def message
14
- @msg || "Tenant Doesn't exist"
15
- end
16
-
17
- def reset_tenant_id
18
- PgRls.connection_class.connection.execute('RESET rls.tenant_id')
19
- end
20
12
  end
21
13
  end
22
14
  end
@@ -1,5 +1,43 @@
1
1
  # frozen_string_literal: true
2
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
+
3
41
  module PgRls
4
42
  module Middleware
5
43
  # Set RLS if sessions present.
@@ -26,7 +64,7 @@ module PgRls
26
64
  cookie_string = env['HTTP_COOKIE']
27
65
  return if cookie_string.nil?
28
66
 
29
- cookie_regex = /#{PgRls.session_key}=([^;]+)/
67
+ cookie_regex = /#{PgRls.session_key_prefix}=([^;]+)/
30
68
  match = cookie_regex.match(cookie_string)
31
69
  match[1] if match
32
70
  end
@@ -36,8 +74,15 @@ module PgRls
36
74
 
37
75
  return if cookie.blank?
38
76
 
39
- sessions = Rails.cache.read("#{PgRls.session_prefix}#{Digest::SHA256.hexdigest(cookie)}")
40
- sessions['_tenant'] if sessions.present?
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
41
86
  end
42
87
 
43
88
  def rails_active_storage_request?(env)
@@ -7,9 +7,18 @@ module PgRls
7
7
  # Set PgRls Policies
8
8
  class Client
9
9
  def call(_job_class, msg, _queue, _redis_pool)
10
- msg['pg_rls'] = PgRls::Tenant.fetch.id
10
+ load_tenant_attribute!(msg)
11
11
  yield
12
12
  end
13
+
14
+ def load_tenant_attribute!(msg)
15
+ if PgRls.username == PgRls.current_db_username
16
+ tenant = PgRls::Tenant.fetch!
17
+ msg['pg_rls'] = tenant.id
18
+ else
19
+ msg['admin'] = true
20
+ end
21
+ end
13
22
  end
14
23
  end
15
24
  end
@@ -7,7 +7,11 @@ module PgRls
7
7
  # Set PgRls Policies
8
8
  class Server
9
9
  def call(_job_instance, msg, _queue, &)
10
- PgRls::Tenant.with_tenant!(msg['pg_rls'], &)
10
+ if msg['admin']
11
+ PgRls.admin_execute(&)
12
+ else
13
+ PgRls::Tenant.with_tenant!(msg['pg_rls'], &)
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -16,13 +16,13 @@ module PgRls
16
16
 
17
17
  private
18
18
 
19
- def switch_tenant!(&block)
19
+ def switch_tenant!
20
20
  fetched_tenant = session[:_tenant] || current_tenant
21
- return block.call if PgRls::Tenant.fetch.present?
21
+ return yield if PgRls::Tenant.fetch.present?
22
22
 
23
23
  Tenant.with_tenant!(fetched_tenant) do |tenant|
24
24
  session[:_tenant] = tenant
25
- block.call(tenant)
25
+ yield(tenant)
26
26
  end
27
27
  rescue NoMethodError
28
28
  session[:_tenant] = nil
@@ -32,7 +32,7 @@ module PgRls
32
32
  def switch_tenant_by_resource!(resource = nil)
33
33
  Tenant.switch!(resource)
34
34
  session[:_tenant] = resource
35
- rescue PgRls::Errors::TenantNotFound, ActiveRecord::RecordNotFound
35
+ rescue PgRls::Errors::TenantNotFound
36
36
  Tenant.switch(session[:_tenant])
37
37
  rescue NoMethodError
38
38
  session[:tenant] = nil
@@ -5,7 +5,7 @@ module PgRls
5
5
  # Down Schema Statements
6
6
  module DownStatements
7
7
  def drop_rls_user
8
- ActiveRecord::Migration.execute <<~SQL
8
+ ActiveRecord::Migration.execute <<~SQL.squish
9
9
  DROP OWNED BY #{PgRls.username};
10
10
  REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM #{PgRls.username};
11
11
  REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM #{PgRls.username};
@@ -23,28 +23,28 @@ module PgRls
23
23
  end
24
24
 
25
25
  def detach_blocking_function(table_name)
26
- ActiveRecord::Migration.execute <<-SQL
26
+ ActiveRecord::Migration.execute <<-SQL.squish
27
27
  DROP TRIGGER IF EXISTS id_safe_guard
28
28
  ON #{table_name};
29
29
  SQL
30
30
  end
31
31
 
32
32
  def detach_trigger_function(table_name)
33
- ActiveRecord::Migration.execute <<-SQL
33
+ ActiveRecord::Migration.execute <<-SQL.squish
34
34
  DROP TRIGGER IF EXISTS tenant_id_setter
35
35
  ON #{table_name};
36
36
  SQL
37
37
  end
38
38
 
39
39
  def drop_rls_column(table_name)
40
- ActiveRecord::Migration.execute <<-SQL
40
+ ActiveRecord::Migration.execute <<-SQL.squish
41
41
  ALTER TABLE #{table_name}
42
42
  DROP COLUMN IF EXISTS tenant_id;
43
43
  SQL
44
44
  end
45
45
 
46
46
  def drop_rls_policy(table_name)
47
- ActiveRecord::Migration.execute <<-SQL
47
+ ActiveRecord::Migration.execute <<-SQL.squish
48
48
  DROP POLICY #{table_name}_#{PgRls.username} ON #{table_name};
49
49
  ALTER TABLE #{table_name} DISABLE ROW LEVEL SECURITY;
50
50
  SQL
@@ -10,17 +10,17 @@ module PgRls
10
10
  include UpStatements
11
11
  include DownStatements
12
12
 
13
- def create_rls_tenant_table(table_name, **options, &)
13
+ def create_rls_tenant_table(table_name, **, &)
14
14
  create_rls_user
15
15
  create_rls_setter_function
16
16
  create_rls_blocking_function
17
- create_table(table_name, **options, &)
17
+ create_table(table_name, **, &)
18
18
  add_rls_column_to_tenant_table(table_name)
19
19
  append_blocking_function(table_name)
20
20
  end
21
21
 
22
- def create_rls_table(table_name, **options, &)
23
- create_table(table_name, **options, &)
22
+ def create_rls_table(table_name, **, &)
23
+ create_table(table_name, **, &)
24
24
  add_rls_column(table_name)
25
25
  create_rls_policy(table_name)
26
26
  append_trigger_function(table_name)