pg_rls 0.0.1.1 → 0.0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4adb2a4dc970c7945eea48bae37f3fe6153607ac61bfe69e5190a4f1b0d3996b
4
- data.tar.gz: 6c223603207bb885e56b4e2340c0e737747418cfeca0fea9a0cda191385cc945
3
+ metadata.gz: d7e3e80029653fbeffba3ab6ab495273c5eabfe8a8f7ca2250b55d58621652d1
4
+ data.tar.gz: c789d0607cff7d73be8eba6205258b85dba70917a28093c800a70c7ca3846c39
5
5
  SHA512:
6
- metadata.gz: 58a6a9f87ad417a3ce1783211afe6189fec242f7488cb238d9ec416c71b960eac7263b1c90cb6afa03460ab0b393d7af510e40b6d5d78484d4e56a89ba742982
7
- data.tar.gz: bc5efaefc28e0b965ebeebc27151b96324399275f96a5ae473ea2336e220ff10f646490b35cbd3d7d5d92cefc55b88025502a8619bb32000ebb32bbbaefaf506
6
+ metadata.gz: c6c19274c02a6de2d07a48786b5bfbf80e7ba2107270b88930f212ab8c0798f1128be85e4f2d300cce8e6aff70b8978dd5e5ea6dc544f25b92faa1896cc8585d
7
+ data.tar.gz: 37be65bcfcc8af3cb23a4c5e6ed5cc761bba6225f1853de6f7692f5e895b74135dacf01984127a54a569966127c9ff79b8b66531aa05ca3bb724dac06ac3baa0
data/README.md CHANGED
@@ -63,7 +63,7 @@ Or install it yourself with:
63
63
  ```bash
64
64
  rails generate pg_rls:install company #=> where company eq tenant model name
65
65
  ```
66
- You can change company to anything you'd like, for example `tenant`
66
+ You can change company to anything you'd like, for example, `tenant`
67
67
  This will generate the model and inject all the required code
68
68
 
69
69
  For any new model that needs to be under rls, you can generate it by writing
@@ -77,7 +77,7 @@ You can swtich to another tenant by using
77
77
  ```ruby
78
78
  PgRls::Tenant.switch :app #=> where app eq tenant name
79
79
  ```
80
-
80
+ Don't forget to update how you want `PgRls` to find your tenant, you can set multiple options by modifying `api/config/initializers/pg_rls.rb` `search_methods`
81
81
  ### Testing
82
82
 
83
83
  Many application uses some sort of database cleaner before running thair spec so on each test that we run we'll have an empty state. Usually, those gems clear our user configuration for the database. To solve this issue, we must implement the following:
@@ -90,8 +90,10 @@ Many application uses some sort of database cleaner before running thair spec so
90
90
 
91
91
  config.before(:suite) do
92
92
  # Create A Default Tenant and Grant Test User Credentials
93
- PgRls::Test::PreparedDatabase.grant_user_credentials
93
+ PgRls::Database::Prepared.grant_user_credentials
94
+ # Create the tenant which in this example is company and we are using FactoryBot
94
95
  FactoryBot.create(:company, subdomain: 'app')
96
+ # In this default case our initializer is set to search by subdomain so will use it
95
97
  PgRls::Tenant.switch :app
96
98
  end
97
99
 
@@ -11,34 +11,86 @@ module PgRls
11
11
 
12
12
  source_root File.expand_path('./templates', __dir__)
13
13
 
14
- def create_migration_file
15
- migration_template migration_template_path, "#{migration_path}/#{file_sub_name}_#{table_name}.rb",
14
+ def check_class_collision; end
15
+
16
+ def create_migration_file; end
17
+
18
+ def migration_exist?
19
+ @migration_exist ||= Dir.glob("#{migration_path}/*create_#{table_name}.rb").present?
20
+ end
21
+
22
+ def create_tenant_migration_file
23
+ return if migration_exist?
24
+
25
+ migration_template create_migration_template_path,
26
+ "#{migration_path}/#{create_file_sub_name}_#{table_name}.rb",
27
+ migration_version: migration_version
28
+ end
29
+
30
+ def convert_tenant_migration_file
31
+ return unless migration_exist?
32
+
33
+ migration_template convert_migration_template_path,
34
+ "#{migration_path}/#{convert_file_sub_name}_#{table_name}.rb",
35
+ migration_version: migration_version
36
+
37
+ return if installation_in_progress?
38
+
39
+ migration_template 'convert_migration_backport.rb.tt',
40
+ "#{migration_path}/pg_rls_backport_#{table_name}.rb",
16
41
  migration_version: migration_version
17
42
  end
18
43
 
19
44
  def create_model_file
45
+ return if migration_exist?
46
+
20
47
  generate_abstract_class if database && !parent
21
- template model_template_path, File.join('app/models', class_path, "#{file_name}.rb")
48
+
49
+ template model_template_path, model_file
50
+ end
51
+
52
+ def inject_method_to_model
53
+ return unless installation_in_progress?
54
+
55
+ gsub_file(model_file, /Class #{class_name} < #{parent_class_name.classify}/mi) do |match|
56
+ "#{match}\n def self.current\n PgRls::Tenant.fetch\n end\n"
57
+ end
58
+ end
59
+
60
+ def model_file
61
+ File.join('app/models', class_path, "#{file_name}.rb")
22
62
  end
23
63
 
24
- def migration_template_path
64
+ def create_migration_template_path
25
65
  return 'init_migration.rb.tt' if installation_in_progress?
26
66
 
27
67
  'migration.rb.tt'
28
68
  end
29
69
 
70
+ def convert_migration_template_path
71
+ return 'init_convert_migration.rb.tt' if installation_in_progress?
72
+
73
+ 'convert_migration.rb.tt'
74
+ end
75
+
30
76
  def model_template_path
31
77
  return 'init_model.rb.tt' if installation_in_progress?
32
78
 
33
79
  'model.rb.tt'
34
80
  end
35
81
 
36
- def file_sub_name
37
- return 'pg_rls_tenant_create' if installation_in_progress?
82
+ def create_file_sub_name
83
+ return 'pg_rls_create_tenant' if installation_in_progress?
38
84
 
39
85
  'pg_rls_create'
40
86
  end
41
87
 
88
+ def convert_file_sub_name
89
+ return 'pg_rls_convert_tenant' if installation_in_progress?
90
+
91
+ 'pg_rls_convert'
92
+ end
93
+
42
94
  def installation_in_progress?
43
95
  shell.base.class.name.include?('Install')
44
96
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PgRlsConvert<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def up
5
+ convert_to_rls_table :<%= table_name %>
6
+ end
7
+
8
+ def down
9
+ revert_rls_table :<%= table_name %>
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PgRlsBackport<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def up
5
+ PgRls.establish_default_connection
6
+
7
+ # Suggested Code:
8
+ # PgRls.all_tenants do |tenant|
9
+ # tenant.<%= table_name %>.in_batches(of: 100) do |<%= table_name %>|
10
+ # <%= table_name %>.each { |<%= table_name.singularize %>| <%= table_name.singularize %>.update_attribute('tenant_id', tenant.tenant_id) }
11
+ # end
12
+ # end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class PgRlsConvertTenant<%= PgRls.table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def up
5
+ convert_to_rls_tenant_table :<%= table_name %>
6
+ end
7
+
8
+ def down
9
+ revert_rls_tenant_table :<%= table_name %>
10
+ end
11
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class PgRlsTenantCreate<%= PgRls.table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
3
+ class PgRlsCreateTenant<%= PgRls.table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
4
  def up
5
5
  create_rls_tenant_table :<%= table_name %>, id: :uuid do |t|
6
6
  t.string :name
@@ -8,4 +8,5 @@ PgRls.setup do |config|
8
8
  # Do not remove this value after initialization
9
9
  config.class_name = :<%= PgRls.class_name %>
10
10
  config.table_name = :<%= PgRls.table_name %>
11
+ config.search_methods = <%= PgRls.search_methods %>
11
12
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRls
4
- module Test
4
+ module Database
5
5
  # Prepare database for test unit
6
- module PreparedDatabase
6
+ module Prepared
7
7
  class << self
8
8
  def grant_user_credentials(name: PgRls::SECURE_USERNAME)
9
9
  return unless Rails.env.test?
@@ -5,7 +5,13 @@ module PgRls
5
5
  # Down Schema Statements
6
6
  module DownStatements
7
7
  def drop_rls_user
8
- ActiveRecord::Migration.execute "DROP USER #{PgRls::SECURE_USERNAME};"
8
+ ActiveRecord::Migration.execute <<~SQL
9
+ DROP OWNED BY #{PgRls::SECURE_USERNAME};
10
+ REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM #{PgRls::SECURE_USERNAME};
11
+ REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM #{PgRls::SECURE_USERNAME};
12
+ REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM #{PgRls::SECURE_USERNAME};
13
+ DROP USER #{PgRls::SECURE_USERNAME};
14
+ SQL
9
15
  end
10
16
 
11
17
  def drop_rls_blocking_function
@@ -27,11 +27,11 @@ module PgRls
27
27
  end
28
28
 
29
29
  def drop_rls_tenant_table(table_name)
30
- drop_rls_user
31
30
  drop_rls_setter_function
32
31
  detach_blocking_function(table_name)
33
32
  drop_table(table_name)
34
33
  drop_rls_blocking_function
34
+ drop_rls_user
35
35
  end
36
36
 
37
37
  def drop_rls_table(table_name)
@@ -39,6 +39,34 @@ module PgRls
39
39
  drop_rls_policy(table_name)
40
40
  drop_table(table_name)
41
41
  end
42
+
43
+ def convert_to_rls_tenant_table(table_name, **_options)
44
+ create_rls_user(password: PgRls.database_configuration['password'])
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
42
70
  end
43
71
  end
44
72
  end
@@ -12,6 +12,9 @@ module PgRls
12
12
  ALTER DEFAULT PRIVILEGES IN SCHEMA public
13
13
  GRANT SELECT, INSERT, UPDATE, DELETE
14
14
  ON TABLES TO #{name};
15
+ GRANT SELECT, INSERT, UPDATE, DELETE
16
+ ON ALL TABLES IN SCHEMA public#{' '}
17
+ TO #{name};
15
18
  SQL
16
19
  end
17
20
 
@@ -12,6 +12,8 @@ module PgRls
12
12
  private
13
13
 
14
14
  def establish_secure_connection
15
+ return if PgRls.default_connection?
16
+
15
17
  return if secure_connection_established?
16
18
 
17
19
  PgRls.establish_new_connection
data/lib/pg_rls/tenant.rb CHANGED
@@ -6,21 +6,19 @@ module PgRls
6
6
  class << self
7
7
  def switch(resource)
8
8
  connection_adapter = PgRls.connection_class
9
- tenant = tenant_by_subdomain_uuid_or_tenant_id(resource)
9
+ find_tenant(resource)
10
10
  connection_adapter.connection.execute(format('SET rls.tenant_id = %s',
11
11
  connection_adapter.connection.quote(tenant.tenant_id)))
12
12
  "RLS changed to '#{tenant.name}'"
13
13
  rescue StandardError => e
14
14
  puts 'connection was not made'
15
- puts e
15
+ puts @error || e
16
16
  end
17
17
 
18
- def tenant
19
- PgRls.class_name.to_s.camelize.constantize
20
- end
18
+ attr_reader :tenant
21
19
 
22
20
  def fetch
23
- tenant.find_by_tenant_id(
21
+ @fetch ||= tenant.find_by_tenant_id(
24
22
  PgRls.connection_class.connection.execute(
25
23
  "SELECT current_setting('rls.tenant_id')"
26
24
  ).getvalue(0, 0)
@@ -29,8 +27,14 @@ module PgRls
29
27
  'no tenant is selected'
30
28
  end
31
29
 
32
- def tenant_by_subdomain_uuid_or_tenant_id(resource)
33
- tenant.find_by_subdomain(resource) || tenant.find_by_id(resource) || tenant.find_by_tenant_id(resource)
30
+ def find_tenant(resource)
31
+ @tenant = nil
32
+
33
+ PgRls.search_methods.each do |method|
34
+ @tenant ||= PgRls.main_model.send("find_by_#{method}", resource)
35
+ rescue NoMethodError => e
36
+ @error = e
37
+ end
34
38
  end
35
39
  end
36
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRls
4
- VERSION = '0.0.1.1'
4
+ VERSION = '0.0.1.2'
5
5
  end
data/lib/pg_rls.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'active_record'
4
4
  require 'forwardable'
5
5
  require_relative 'pg_rls/version'
6
- require_relative 'pg_rls/test/prepared_database'
6
+ require_relative 'pg_rls/database/prepared'
7
7
  require_relative 'pg_rls/schema/statements'
8
8
  require_relative 'pg_rls/tenant'
9
9
  require_relative 'pg_rls/secure_connection'
@@ -17,12 +17,13 @@ module PgRls
17
17
  class << self
18
18
  extend Forwardable
19
19
 
20
- WRITER_METHODS = %i[table_name class_name].freeze
20
+ WRITER_METHODS = %i[table_name class_name search_methods].freeze
21
21
  READER_METHODS = %i[
22
- connection_class database_configuration execute table_name class_name
22
+ connection_class database_configuration execute table_name class_name search_methods
23
23
  ].freeze
24
24
  DELEGATORS_METHODS = %i[
25
- connection_class database_configuration execute table_name class_name
25
+ connection_class database_configuration execute table_name search_methods
26
+ class_name all_tenants main_model establish_default_connection
26
27
  ].freeze
27
28
 
28
29
  attr_writer(*WRITER_METHODS)
@@ -50,8 +51,29 @@ module PgRls
50
51
  )
51
52
  end
52
53
 
54
+ def establish_default_connection
55
+ @default_connection = true
56
+ end
57
+
58
+ def default_connection?
59
+ @default_connection
60
+ end
61
+
62
+ def main_model
63
+ class_name.to_s.camelize.constantize
64
+ end
65
+
66
+ def all_tenants
67
+ main_model.all.each do |tenant|
68
+ allowed_search_fields = search_methods.map(&:to_s).intersection(main_model.column_names)
69
+ Tenant.switch tenant.send(allowed_search_fields.first)
70
+
71
+ yield(tenant) if block_given?
72
+ end
73
+ end
74
+
53
75
  def current_connection_username
54
- PgRls.connection_class.connection_db_config.configuration_hash[:username]
76
+ connection_class.connection_db_config.configuration_hash[:username]
55
77
  end
56
78
 
57
79
  def execute(query)
@@ -69,4 +91,7 @@ module PgRls
69
91
 
70
92
  mattr_accessor :class_name
71
93
  @@class_name = 'Company'
94
+
95
+ mattr_accessor :search_methods
96
+ @@search_methods = %i[subdomain id tenant_id]
72
97
  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.1.1
4
+ version: 0.0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Laloush
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-30 00:00:00.000000000 Z
11
+ date: 2022-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -47,6 +47,9 @@ files:
47
47
  - lib/generators/pg_rls.rb
48
48
  - lib/generators/pg_rls/active_record/active_record_generator.rb
49
49
  - lib/generators/pg_rls/active_record/templates/abstract_base_class.rb.tt
50
+ - lib/generators/pg_rls/active_record/templates/convert_migration.rb.tt
51
+ - lib/generators/pg_rls/active_record/templates/convert_migration_backport.rb.tt
52
+ - lib/generators/pg_rls/active_record/templates/init_convert_migration.rb.tt
50
53
  - lib/generators/pg_rls/active_record/templates/init_migration.rb.tt
51
54
  - lib/generators/pg_rls/active_record/templates/init_model.rb.tt
52
55
  - lib/generators/pg_rls/active_record/templates/migration.rb.tt
@@ -57,18 +60,19 @@ files:
57
60
  - lib/generators/templates/README
58
61
  - lib/generators/templates/pg_rls.rb.tt
59
62
  - lib/pg_rls.rb
63
+ - lib/pg_rls/database/prepared.rb
60
64
  - lib/pg_rls/multi_tenancy.rb
61
65
  - lib/pg_rls/schema/down_statements.rb
62
66
  - lib/pg_rls/schema/statements.rb
63
67
  - lib/pg_rls/schema/up_statements.rb
64
68
  - lib/pg_rls/secure_connection.rb
65
69
  - lib/pg_rls/tenant.rb
66
- - lib/pg_rls/test/prepared_database.rb
67
70
  - lib/pg_rls/version.rb
68
71
  homepage: https://github.com/Dandush03/pg_rls
69
72
  licenses:
70
73
  - MIT
71
- metadata: {}
74
+ metadata:
75
+ rubygems_mfa_required: 'true'
72
76
  post_install_message:
73
77
  rdoc_options: []
74
78
  require_paths:
@@ -84,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
88
  - !ruby/object:Gem::Version
85
89
  version: '0'
86
90
  requirements: []
87
- rubygems_version: 3.2.27
91
+ rubygems_version: 3.2.22
88
92
  signing_key:
89
93
  specification_version: 4
90
94
  summary: Write a short summary, because RubyGems requires one.