pg_rls 0.0.1.1 → 0.0.1.4.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/README.md +32 -2
- data/lib/generators/pg_rls/active_record/active_record_generator.rb +58 -6
- data/lib/generators/pg_rls/active_record/templates/convert_migration.rb.tt +11 -0
- data/lib/generators/pg_rls/active_record/templates/convert_migration_backport.rb.tt +12 -0
- data/lib/generators/pg_rls/active_record/templates/init_convert_migration.rb.tt +11 -0
- data/lib/generators/pg_rls/active_record/templates/init_migration.rb.tt +1 -1
- data/lib/generators/templates/pg_rls.rb.tt +1 -0
- data/lib/pg_rls/Rakefile +6 -0
- data/lib/pg_rls/database/prepared.rb +38 -0
- data/lib/pg_rls/database/tasks/admin_database.rake +117 -0
- data/lib/pg_rls/railtie.rb +16 -0
- data/lib/pg_rls/schema/down_statements.rb +7 -1
- data/lib/pg_rls/schema/statements.rb +29 -1
- data/lib/pg_rls/schema/up_statements.rb +7 -0
- data/lib/pg_rls/secure_connection.rb +2 -0
- data/lib/pg_rls/tenant.rb +14 -9
- data/lib/pg_rls/version.rb +1 -1
- data/lib/pg_rls.rb +46 -8
- metadata +12 -5
- data/lib/pg_rls/test/prepared_database.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa0740a77d93fc9f87723797b3bca421e957a5661e4f82211762e21e9a29e53e
|
4
|
+
data.tar.gz: 47c18a7af64db89155056eaa370131134581f56769730865bf4c9e7781208ca2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0cfdbfb358da830cabf1fae1406e52d208960331f9a871aef2829a9c18d7d5f31d0e6aa5c0d96942148bb6c64060faa875ec11dfe8dd0d9e2da4d14e41637b0
|
7
|
+
data.tar.gz: f420cf2bfc326cc3b8404f5059a9e1e6b398c377319939d196539be4e2aa4c43d88d27dfd579ca0ceea71cded49a78956687798a88b788a39c5076f2de88f970
|
data/README.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
<!--
|
2
|
+
Title: PgRls Rails
|
3
|
+
Description: rails multitenancy with pg rls
|
4
|
+
Author: dandush03
|
5
|
+
-->
|
6
|
+
<meta name="google-site-verification" content="Mc1vBv8PRYPw_cdd3EiKhF2vlOeIEIk3VYhAg75ertI" />
|
7
|
+
|
1
8
|
[![Contributors][contributors-shield]][contributors-url]
|
2
9
|
[![Forks][forks-shield]][forks-url]
|
3
10
|
[![Stargazers][stars-shield]][stars-url]
|
@@ -63,7 +70,7 @@ Or install it yourself with:
|
|
63
70
|
```bash
|
64
71
|
rails generate pg_rls:install company #=> where company eq tenant model name
|
65
72
|
```
|
66
|
-
You can change company to anything you'd like, for example `tenant`
|
73
|
+
You can change company to anything you'd like, for example, `tenant`
|
67
74
|
This will generate the model and inject all the required code
|
68
75
|
|
69
76
|
For any new model that needs to be under rls, you can generate it by writing
|
@@ -77,7 +84,28 @@ You can swtich to another tenant by using
|
|
77
84
|
```ruby
|
78
85
|
PgRls::Tenant.switch :app #=> where app eq tenant name
|
79
86
|
```
|
87
|
+
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`
|
88
|
+
|
89
|
+
You can add the following configuration to your Database Config File to improve performance and remove `PgRls::SecureConnection` from `aplication_record.rb`
|
90
|
+
|
91
|
+
```yml
|
92
|
+
# app/config/database.yml
|
93
|
+
<% def db_username
|
94
|
+
return PgRls::SECURE_USERNAME unless ENV['AS_DB_ADMIN']
|
95
|
+
|
96
|
+
Rails.application.credentials.dig(:database, :server_1, :username)
|
97
|
+
end %>
|
98
|
+
|
99
|
+
...
|
100
|
+
|
101
|
+
development:
|
102
|
+
<<: *default
|
103
|
+
database: example_development
|
104
|
+
username: <%= db_username %> # Apply this to production and all env including tests
|
80
105
|
|
106
|
+
...
|
107
|
+
|
108
|
+
```
|
81
109
|
### Testing
|
82
110
|
|
83
111
|
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 +118,10 @@ Many application uses some sort of database cleaner before running thair spec so
|
|
90
118
|
|
91
119
|
config.before(:suite) do
|
92
120
|
# Create A Default Tenant and Grant Test User Credentials
|
93
|
-
PgRls::
|
121
|
+
PgRls::Database::Prepared.grant_user_credentials
|
122
|
+
# Create the tenant which in this example is company and we are using FactoryBot
|
94
123
|
FactoryBot.create(:company, subdomain: 'app')
|
124
|
+
# In this default case our initializer is set to search by subdomain so will use it
|
95
125
|
PgRls::Tenant.switch :app
|
96
126
|
end
|
97
127
|
|
@@ -11,34 +11,86 @@ module PgRls
|
|
11
11
|
|
12
12
|
source_root File.expand_path('./templates', __dir__)
|
13
13
|
|
14
|
-
def
|
15
|
-
|
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
|
-
|
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
|
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
|
37
|
-
return '
|
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,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class PgRlsBackport<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
|
4
|
+
def up
|
5
|
+
# Suggested Code:
|
6
|
+
# PgRls.all_tenants do |tenant|
|
7
|
+
# tenant.<%= table_name %>.in_batches(of: 100) do |<%= table_name %>|
|
8
|
+
# <%= table_name %>.each { |<%= table_name.singularize %>| <%= table_name.singularize %>.update_attribute('tenant_id', tenant.tenant_id) }
|
9
|
+
# end
|
10
|
+
# end
|
11
|
+
end
|
12
|
+
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
|
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
|
data/lib/pg_rls/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgRls
|
4
|
+
module Database
|
5
|
+
# Prepare database for test unit
|
6
|
+
module Prepared
|
7
|
+
class << self
|
8
|
+
def grant_user_credentials(name: PgRls::SECURE_USERNAME, password: 'password')
|
9
|
+
return unless Rails.env.test? || PgRls.default_connection?
|
10
|
+
|
11
|
+
PgRls.admin_execute <<-SQL
|
12
|
+
DO
|
13
|
+
$do$
|
14
|
+
BEGIN
|
15
|
+
IF NOT EXISTS (
|
16
|
+
SELECT FROM pg_catalog.pg_roles AS r
|
17
|
+
WHERE r.rolname = '#{name}') THEN
|
18
|
+
|
19
|
+
CREATE USER #{name} WITH PASSWORD '#{password}';
|
20
|
+
END IF;
|
21
|
+
END
|
22
|
+
$do$;
|
23
|
+
GRANT USAGE ON SCHEMA public TO #{name};
|
24
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
25
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
26
|
+
ON TABLES TO #{name};
|
27
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
28
|
+
ON ALL TABLES IN SCHEMA public
|
29
|
+
TO #{name};
|
30
|
+
GRANT USAGE, SELECT
|
31
|
+
ON ALL SEQUENCES IN SCHEMA public
|
32
|
+
TO #{name};
|
33
|
+
SQL
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# OVERIDE RAILS TASK
|
4
|
+
Rake::TaskManager.class_eval do
|
5
|
+
def alias_task(fq_name)
|
6
|
+
new_name = "#{fq_name}:original"
|
7
|
+
@tasks[new_name] = @tasks.delete(fq_name)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def alias_task(fq_name)
|
12
|
+
Rake.application.alias_task(fq_name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def override_task(*args, &block)
|
16
|
+
name, _params, _deps = Rake.application.resolve_args(args.dup)
|
17
|
+
fq_name = Rake.application.instance_variable_get(:@scope).to_a.reverse.push(name).join(':')
|
18
|
+
alias_task(fq_name)
|
19
|
+
Rake::Task.define_task(*args, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
namespace :db do
|
23
|
+
override_task create: :load_config do
|
24
|
+
system('AS_DB_ADMIN=true rake db:create:original')
|
25
|
+
end
|
26
|
+
|
27
|
+
override_task drop: :load_config do
|
28
|
+
system('AS_DB_ADMIN=true rake db:drop:original')
|
29
|
+
end
|
30
|
+
|
31
|
+
override_task migrate: :load_config do
|
32
|
+
system('AS_DB_ADMIN=true rake db:migrate:original')
|
33
|
+
end
|
34
|
+
|
35
|
+
override_task rollback: :load_config do
|
36
|
+
system('AS_DB_ADMIN=true rake db:rollback:original')
|
37
|
+
end
|
38
|
+
|
39
|
+
override_task prepare: :load_config do
|
40
|
+
system('AS_DB_ADMIN=true rake db:prepare:original')
|
41
|
+
end
|
42
|
+
|
43
|
+
override_task setup: :load_config do
|
44
|
+
system('AS_DB_ADMIN=true rake db:setup:original')
|
45
|
+
end
|
46
|
+
|
47
|
+
override_task prepare: :load_config do
|
48
|
+
system('AS_DB_ADMIN=true rake db:reset:original')
|
49
|
+
end
|
50
|
+
|
51
|
+
override_task purge: :load_config do
|
52
|
+
system('AS_DB_ADMIN=true rake db:purge:original')
|
53
|
+
end
|
54
|
+
|
55
|
+
override_task abort_if_pending_migrations: :load_config do
|
56
|
+
system('AS_DB_ADMIN=true rake db:abort_if_pending_migrations:original')
|
57
|
+
end
|
58
|
+
|
59
|
+
override_task seed: :load_config do
|
60
|
+
system('AS_DB_ADMIN=true rake db:seed:original')
|
61
|
+
end
|
62
|
+
|
63
|
+
namespace :test do
|
64
|
+
override_task create: :load_config do
|
65
|
+
system('AS_DB_ADMIN=true rake db:test:create:original')
|
66
|
+
end
|
67
|
+
|
68
|
+
override_task drop: :load_config do
|
69
|
+
system('AS_DB_ADMIN=true rake db:test:drop:original')
|
70
|
+
end
|
71
|
+
|
72
|
+
override_task prepare: :load_config do
|
73
|
+
system('AS_DB_ADMIN=true rake db:test:prepare:original')
|
74
|
+
end
|
75
|
+
|
76
|
+
override_task setup: :load_config do
|
77
|
+
system('AS_DB_ADMIN=true rake db:test:setup:original')
|
78
|
+
end
|
79
|
+
|
80
|
+
override_task purge: :load_config do
|
81
|
+
system('AS_DB_ADMIN=true rake db:test:purge:original')
|
82
|
+
end
|
83
|
+
|
84
|
+
override_task load_schema: :load_config do
|
85
|
+
system('AS_DB_ADMIN=true rake db:test:load_schema:original')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
namespace :enviroment do
|
90
|
+
override_task set: :load_config do
|
91
|
+
system('AS_DB_ADMIN=true rake db:enviroment:set:original')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
namespace :schema do
|
96
|
+
override_task load: :load_config do
|
97
|
+
system('AS_DB_ADMIN=true rake db:schema:load:original')
|
98
|
+
PgRls.admin_execute do
|
99
|
+
PgRls.execute <<-SQL
|
100
|
+
DROP ROLE IF EXISTS #{PgRls::SECURE_USERNAME};
|
101
|
+
CREATE USER #{PgRls::SECURE_USERNAME} WITH PASSWORD '#{PgRls.database_configuration['password']}';
|
102
|
+
GRANT ALL PRIVILEGES ON TABLE schema_migrations TO #{PgRls::SECURE_USERNAME};
|
103
|
+
GRANT USAGE ON SCHEMA public TO #{PgRls::SECURE_USERNAME};
|
104
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
105
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
106
|
+
ON TABLES TO #{PgRls::SECURE_USERNAME};
|
107
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
108
|
+
ON ALL TABLES IN SCHEMA public
|
109
|
+
TO #{PgRls::SECURE_USERNAME};
|
110
|
+
GRANT USAGE, SELECT
|
111
|
+
ON ALL SEQUENCES IN SCHEMA public
|
112
|
+
TO #{PgRls::SECURE_USERNAME};
|
113
|
+
SQL
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../pg_rls'
|
4
|
+
require 'rails'
|
5
|
+
|
6
|
+
module PgRls
|
7
|
+
# Extend Rails Railties
|
8
|
+
class Railtie < Rails::Railtie
|
9
|
+
railtie_name :my_gem
|
10
|
+
|
11
|
+
rake_tasks do
|
12
|
+
path = File.dirname(__FILE__)
|
13
|
+
Dir.glob("#{path}/database/tasks/**/*.rake").each { |f| load f }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -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
|
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
|
@@ -9,9 +9,16 @@ module PgRls
|
|
9
9
|
DROP ROLE IF EXISTS #{name};
|
10
10
|
CREATE USER #{name} WITH PASSWORD '#{password}';
|
11
11
|
GRANT ALL PRIVILEGES ON TABLE schema_migrations TO #{name};
|
12
|
+
GRANT USAGE ON SCHEMA public TO #{name};
|
12
13
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
13
14
|
GRANT SELECT, INSERT, UPDATE, DELETE
|
14
15
|
ON TABLES TO #{name};
|
16
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
17
|
+
ON ALL TABLES IN SCHEMA public
|
18
|
+
TO #{name};
|
19
|
+
GRANT USAGE, SELECT
|
20
|
+
ON ALL SEQUENCES IN SCHEMA public
|
21
|
+
TO #{name};
|
15
22
|
SQL
|
16
23
|
end
|
17
24
|
|
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
|
-
|
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
|
-
"RLS changed to '#{tenant.
|
12
|
+
"RLS changed to '#{tenant.send(@method)}'"
|
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
|
-
|
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,15 @@ module PgRls
|
|
29
27
|
'no tenant is selected'
|
30
28
|
end
|
31
29
|
|
32
|
-
def
|
33
|
-
tenant
|
30
|
+
def find_tenant(resource)
|
31
|
+
@tenant = nil
|
32
|
+
|
33
|
+
PgRls.search_methods.each do |method|
|
34
|
+
@method = method
|
35
|
+
@tenant ||= PgRls.main_model.send("find_by_#{method}", resource)
|
36
|
+
rescue NoMethodError => e
|
37
|
+
@error = e
|
38
|
+
end
|
34
39
|
end
|
35
40
|
end
|
36
41
|
end
|
data/lib/pg_rls/version.rb
CHANGED
data/lib/pg_rls.rb
CHANGED
@@ -3,11 +3,12 @@
|
|
3
3
|
require 'active_record'
|
4
4
|
require 'forwardable'
|
5
5
|
require_relative 'pg_rls/version'
|
6
|
-
require_relative 'pg_rls/
|
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'
|
10
10
|
require_relative 'pg_rls/multi_tenancy'
|
11
|
+
require_relative 'pg_rls/railtie' if defined?(Rails)
|
11
12
|
|
12
13
|
# PostgreSQL Row Level Security
|
13
14
|
module PgRls
|
@@ -17,12 +18,13 @@ module PgRls
|
|
17
18
|
class << self
|
18
19
|
extend Forwardable
|
19
20
|
|
20
|
-
WRITER_METHODS = %i[table_name class_name].freeze
|
21
|
+
WRITER_METHODS = %i[table_name class_name search_methods establish_default_connection].freeze
|
21
22
|
READER_METHODS = %i[
|
22
|
-
connection_class database_configuration execute table_name class_name
|
23
|
+
connection_class database_configuration execute table_name class_name search_methods establish_default_connection
|
23
24
|
].freeze
|
24
25
|
DELEGATORS_METHODS = %i[
|
25
|
-
connection_class database_configuration execute table_name
|
26
|
+
connection_class database_configuration execute table_name search_methods
|
27
|
+
class_name all_tenants main_model establish_default_connection
|
26
28
|
].freeze
|
27
29
|
|
28
30
|
attr_writer(*WRITER_METHODS)
|
@@ -50,17 +52,50 @@ module PgRls
|
|
50
52
|
)
|
51
53
|
end
|
52
54
|
|
55
|
+
def admin_execute(query = nil)
|
56
|
+
self.establish_default_connection = true
|
57
|
+
establish_new_connection
|
58
|
+
return yield if block_given?
|
59
|
+
|
60
|
+
execute(query)
|
61
|
+
ensure
|
62
|
+
self.establish_default_connection = false
|
63
|
+
establish_new_connection
|
64
|
+
end
|
65
|
+
|
66
|
+
def establish_default_connection=(value)
|
67
|
+
ENV['AS_DB_ADMIN'] = value.to_s
|
68
|
+
@default_connection = value
|
69
|
+
end
|
70
|
+
|
71
|
+
def default_connection?
|
72
|
+
@default_connection
|
73
|
+
end
|
74
|
+
|
75
|
+
def main_model
|
76
|
+
class_name.to_s.camelize.constantize
|
77
|
+
end
|
78
|
+
|
79
|
+
def all_tenants
|
80
|
+
main_model.all.each do |tenant|
|
81
|
+
allowed_search_fields = search_methods.map(&:to_s).intersection(main_model.column_names)
|
82
|
+
Tenant.switch tenant.send(allowed_search_fields.first)
|
83
|
+
|
84
|
+
yield(tenant) if block_given?
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
53
88
|
def current_connection_username
|
54
|
-
|
89
|
+
connection_class.connection_db_config.configuration_hash[:username]
|
55
90
|
end
|
56
91
|
|
57
92
|
def execute(query)
|
58
|
-
|
93
|
+
ActiveRecord::Migration.execute(query)
|
59
94
|
end
|
60
95
|
|
61
96
|
def database_configuration
|
62
|
-
|
63
|
-
config['username'] = PgRls::SECURE_USERNAME
|
97
|
+
database_connection_file[Rails.env].tap do |config|
|
98
|
+
config['username'] = PgRls::SECURE_USERNAME unless default_connection?
|
64
99
|
end
|
65
100
|
end
|
66
101
|
end
|
@@ -69,4 +104,7 @@ module PgRls
|
|
69
104
|
|
70
105
|
mattr_accessor :class_name
|
71
106
|
@@class_name = 'Company'
|
107
|
+
|
108
|
+
mattr_accessor :search_methods
|
109
|
+
@@search_methods = %i[subdomain id tenant_id]
|
72
110
|
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.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Laloush
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-16 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,22 @@ 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/Rakefile
|
64
|
+
- lib/pg_rls/database/prepared.rb
|
65
|
+
- lib/pg_rls/database/tasks/admin_database.rake
|
60
66
|
- lib/pg_rls/multi_tenancy.rb
|
67
|
+
- lib/pg_rls/railtie.rb
|
61
68
|
- lib/pg_rls/schema/down_statements.rb
|
62
69
|
- lib/pg_rls/schema/statements.rb
|
63
70
|
- lib/pg_rls/schema/up_statements.rb
|
64
71
|
- lib/pg_rls/secure_connection.rb
|
65
72
|
- lib/pg_rls/tenant.rb
|
66
|
-
- lib/pg_rls/test/prepared_database.rb
|
67
73
|
- lib/pg_rls/version.rb
|
68
74
|
homepage: https://github.com/Dandush03/pg_rls
|
69
75
|
licenses:
|
70
76
|
- MIT
|
71
|
-
metadata:
|
77
|
+
metadata:
|
78
|
+
rubygems_mfa_required: 'true'
|
72
79
|
post_install_message:
|
73
80
|
rdoc_options: []
|
74
81
|
require_paths:
|
@@ -84,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
91
|
- !ruby/object:Gem::Version
|
85
92
|
version: '0'
|
86
93
|
requirements: []
|
87
|
-
rubygems_version: 3.
|
94
|
+
rubygems_version: 3.3.3
|
88
95
|
signing_key:
|
89
96
|
specification_version: 4
|
90
97
|
summary: Write a short summary, because RubyGems requires one.
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PgRls
|
4
|
-
module Test
|
5
|
-
# Prepare database for test unit
|
6
|
-
module PreparedDatabase
|
7
|
-
class << self
|
8
|
-
def grant_user_credentials(name: PgRls::SECURE_USERNAME)
|
9
|
-
return unless Rails.env.test?
|
10
|
-
|
11
|
-
PgRls.execute <<-SQL
|
12
|
-
GRANT USAGE, SELECT
|
13
|
-
ON ALL SEQUENCES IN SCHEMA public
|
14
|
-
TO #{name};
|
15
|
-
GRANT SELECT, INSERT, UPDATE, DELETE
|
16
|
-
ON ALL TABLES IN SCHEMA public
|
17
|
-
TO #{name};
|
18
|
-
SQL
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|