pg_rls 0.0.1.4.3 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/Rakefile +1 -2
- data/lib/generators/pg_rls/active_record/active_record_generator.rb +16 -10
- data/lib/generators/pg_rls/install_generator.rb +17 -0
- data/lib/generators/templates/README +5 -0
- data/lib/generators/templates/pg_rls.rb.tt +10 -0
- data/lib/pg_rls/Rakefile +1 -0
- data/lib/pg_rls/database/prepared.rb +21 -19
- data/lib/pg_rls/database/tasks/admin_database.rake +95 -37
- data/lib/pg_rls/errors/tenant_not_found.rb +2 -0
- data/lib/pg_rls/multi_tenancy.rb +9 -1
- data/lib/pg_rls/schema/down_statements.rb +6 -6
- data/lib/pg_rls/schema/statements.rb +2 -2
- data/lib/pg_rls/schema/up_statements.rb +20 -17
- data/lib/pg_rls/secure_connection.rb +11 -12
- data/lib/pg_rls/tenant.rb +25 -11
- data/lib/pg_rls/version.rb +1 -1
- data/lib/pg_rls.rb +32 -6
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ee45b6831bde44dff2a29fa6dd8e832e1e55970833bbbd52a9a30982f6b987a
|
4
|
+
data.tar.gz: 83013597d4b42fb6b808c8c1b7e90c58eeb3e8dc231923523a01e6454b52507d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fae8b1317628ddc17c19753eab39937bfc237cda1c0df9b60a9c91bbe58e5203316e305fbe3175d803531e09fc9e43b0e63941cd2153aa9a363a4d4f35e0d308
|
7
|
+
data.tar.gz: a42e6f8ebc4a93739ab3475a9d436748f1c63fc2e4878011a37d9bb46db17fff79bf66bfb4f374f5cabf8496d93bcac024a285687f19bca5f19f3717fd65f05e
|
data/README.md
CHANGED
@@ -91,7 +91,7 @@ You can add the following configuration to your Database Config File to improve
|
|
91
91
|
```yml
|
92
92
|
# app/config/database.yml
|
93
93
|
<% def db_username
|
94
|
-
return PgRls
|
94
|
+
return PgRls.username unless ENV['AS_DB_ADMIN']
|
95
95
|
|
96
96
|
Rails.application.credentials.dig(:database, :server_1, :username)
|
97
97
|
end %>
|
@@ -108,6 +108,8 @@ development:
|
|
108
108
|
```
|
109
109
|
### Testing
|
110
110
|
|
111
|
+
If you are getting `PG::InsufficientPrivilege: ERROR: permission denied ` you can override does permistion by running `RAILS_ENV=test rake db:grant_usage`
|
112
|
+
|
111
113
|
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:
|
112
114
|
|
113
115
|
```ruby
|
@@ -117,8 +119,6 @@ Many application uses some sort of database cleaner before running thair spec so
|
|
117
119
|
# some database cleaning strategy
|
118
120
|
|
119
121
|
config.before(:suite) do
|
120
|
-
# Create A Default Tenant and Grant Test User Credentials
|
121
|
-
PgRls::Database::Prepared.grant_user_credentials
|
122
122
|
# Create the tenant which in this example is company and we are using FactoryBot
|
123
123
|
FactoryBot.create(:company, subdomain: 'app')
|
124
124
|
# In this default case our initializer is set to search by subdomain so will use it
|
@@ -147,7 +147,7 @@ Everyone interacting in the PgRls project's codebases, issue trackers, chat room
|
|
147
147
|
|
148
148
|
## Note
|
149
149
|
Currently we only support subdomain as a searcher but will soon integrate slug/domain and cookies support
|
150
|
-
|
150
|
+
we recommed the use of ``
|
151
151
|
## Show your support
|
152
152
|
|
153
153
|
Give a ⭐️ if you like this project!
|
data/Rakefile
CHANGED
@@ -20,19 +20,25 @@ module PgRls
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def create_tenant_migration_file
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
if creating?
|
24
|
+
migration_template(create_migration_template_path,
|
25
|
+
"#{migration_path}/#{create_file_sub_name}_#{table_name}.rb",
|
26
|
+
migration_version: migration_version)
|
27
|
+
end
|
26
28
|
end
|
27
29
|
|
28
30
|
def convert_tenant_migration_file
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
unless creating?
|
32
|
+
migration_template(convert_migration_template_path,
|
33
|
+
"#{migration_path}/#{convert_file_sub_name}_#{table_name}.rb",
|
34
|
+
migration_version: migration_version)
|
35
|
+
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
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: migration_version)
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
44
|
def create_model_file
|
@@ -103,7 +109,7 @@ module PgRls
|
|
103
109
|
|
104
110
|
protected
|
105
111
|
|
106
|
-
def migration_action
|
112
|
+
def migration_action = 'add'
|
107
113
|
end
|
108
114
|
end
|
109
115
|
end
|
@@ -16,8 +16,12 @@ module PgRls
|
|
16
16
|
end
|
17
17
|
super
|
18
18
|
end
|
19
|
+
APPLICATION_LINE = 'class Application < Rails::Application'
|
20
|
+
APPLICATION_PATH = 'config/application.rb'
|
21
|
+
|
19
22
|
APPLICATION_RECORD_LINE = 'class ApplicationRecord < ActiveRecord::Base'
|
20
23
|
APPLICATION_RECORD_PATH = 'app/models/application_record.rb'
|
24
|
+
|
21
25
|
APPLICATION_CONTROLLER_LINE = 'class ApplicationController < ActionController::Base'
|
22
26
|
APPLICATION_CONTROLLER_PATH = 'app/controllers/application_controller.rb'
|
23
27
|
|
@@ -41,11 +45,20 @@ module PgRls
|
|
41
45
|
def copy_initializer
|
42
46
|
raise MissingORMError, orm_error_message unless options[:orm]
|
43
47
|
|
48
|
+
inject_include_to_application
|
44
49
|
inject_include_to_application_record
|
45
50
|
inject_include_to_application_controller
|
46
51
|
template 'pg_rls.rb.tt', 'config/initializers/pg_rls.rb'
|
47
52
|
end
|
48
53
|
|
54
|
+
def inject_include_to_application
|
55
|
+
return if aplication_already_included?
|
56
|
+
|
57
|
+
gsub_file(APPLICATION_PATH, /(#{Regexp.escape(APPLICATION_LINE)})/mi) do |match|
|
58
|
+
"#{match}\n config.active_record.schema_format = :sql\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
49
62
|
def inject_include_to_application_record
|
50
63
|
return if aplication_record_already_included?
|
51
64
|
|
@@ -70,6 +83,10 @@ module PgRls
|
|
70
83
|
File.readlines(APPLICATION_RECORD_PATH).grep(/include PgRls::SecureConnection/).any?
|
71
84
|
end
|
72
85
|
|
86
|
+
def aplication_already_included?
|
87
|
+
File.readlines(APPLICATION_PATH).grep(/config.active_record.schema_format = :sql/).any?
|
88
|
+
end
|
89
|
+
|
73
90
|
def initialize_error_text
|
74
91
|
<<-ERROR.strip_heredoc
|
75
92
|
ERROR
|
@@ -2,10 +2,15 @@ README
|
|
2
2
|
===============================================================================
|
3
3
|
WARNING!!
|
4
4
|
|
5
|
+
PgRls required that ActiveRecord format migration as SQL
|
6
|
+
|
5
7
|
Once you remove a tenant all of his data would be removed as well
|
6
8
|
|
7
9
|
PgRls::SecureConnection was included to your ApplicationRecord do not remove it
|
8
10
|
|
11
|
+
If you're setting a custom user, make sure to regenerate the structure.sql since
|
12
|
+
Postgresql policies are created on each user
|
13
|
+
|
9
14
|
===============================================================================
|
10
15
|
|
11
16
|
to generate secure model run
|
@@ -9,4 +9,14 @@ PgRls.setup do |config|
|
|
9
9
|
config.class_name = :<%= PgRls.class_name %>
|
10
10
|
config.table_name = :<%= PgRls.table_name %>
|
11
11
|
config.search_methods = <%= PgRls.search_methods %>
|
12
|
+
|
13
|
+
##
|
14
|
+
## Uncomment this lines if you have a custome user per enviroment
|
15
|
+
## don't forget to grant the required privilange in order for it to run
|
16
|
+
##
|
17
|
+
## Remember that PgRls is adding triggers that would set the RLS to the default user
|
18
|
+
## make sure you recreate the structure.sql on each enviroment
|
19
|
+
##
|
20
|
+
# config.username = Rails.application.credentials.dig(:database, :username)
|
21
|
+
# config.password = Rails.application.credentials.dig(:database, :password)
|
12
22
|
end
|
data/lib/pg_rls/Rakefile
CHANGED
@@ -5,31 +5,33 @@ module PgRls
|
|
5
5
|
# Prepare database for test unit
|
6
6
|
module Prepared
|
7
7
|
class << self
|
8
|
-
def grant_user_credentials(name: PgRls
|
9
|
-
return unless Rails.env.test? || PgRls.default_connection?
|
10
|
-
|
8
|
+
def grant_user_credentials(name: PgRls.username, password: PgRls.password, schema: 'public')
|
11
9
|
PgRls.admin_execute <<-SQL
|
12
10
|
DO
|
13
11
|
$do$
|
14
12
|
BEGIN
|
15
13
|
IF NOT EXISTS (
|
16
|
-
SELECT
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
SELECT table_catalog, table_schema, table_name, privilege_type
|
15
|
+
FROM information_schema.table_privileges#{' '}
|
16
|
+
WHERE grantee = '#{name}'
|
17
|
+
) THEN
|
18
|
+
GRANT ALL PRIVILEGES ON TABLE schema_migrations TO #{name};
|
19
|
+
GRANT USAGE ON SCHEMA #{schema} TO #{name};
|
20
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
21
|
+
GRANT USAGE, SELECT
|
22
|
+
ON SEQUENCES TO #{name};
|
23
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
24
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
25
|
+
ON TABLES TO #{name};
|
26
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
27
|
+
ON ALL TABLES IN SCHEMA #{schema}
|
28
|
+
TO #{name};
|
29
|
+
GRANT USAGE, SELECT
|
30
|
+
ON ALL SEQUENCES IN SCHEMA #{schema}
|
31
|
+
TO #{name};
|
32
|
+
END IF;
|
33
|
+
END;
|
22
34
|
$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
35
|
SQL
|
34
36
|
end
|
35
37
|
end
|
@@ -20,97 +20,155 @@ def override_task(*args, &block)
|
|
20
20
|
end
|
21
21
|
|
22
22
|
namespace :db do
|
23
|
+
include PgRls::Schema::UpStatements
|
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
|
+
override_task grant_usage: :load_config do
|
37
|
+
admin_connection do
|
38
|
+
create_rls_user
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
23
42
|
override_task create: :load_config do
|
24
|
-
|
43
|
+
admin_connection do
|
44
|
+
Rake::Task['db:create:original'].invoke
|
45
|
+
end
|
25
46
|
end
|
26
47
|
|
27
48
|
override_task drop: :load_config do
|
28
|
-
|
49
|
+
admin_connection do
|
50
|
+
Rake::Task['db:drop:original'].invoke
|
51
|
+
end
|
29
52
|
end
|
30
53
|
|
31
54
|
override_task migrate: :load_config do
|
32
|
-
|
55
|
+
admin_connection do
|
56
|
+
Rake::Task['db:migrate:original'].invoke
|
57
|
+
end
|
33
58
|
end
|
34
59
|
|
35
60
|
override_task rollback: :load_config do
|
36
|
-
|
61
|
+
admin_connection do
|
62
|
+
Rake::Task['db:rollback:original'].invoke
|
63
|
+
end
|
37
64
|
end
|
38
65
|
|
39
66
|
override_task prepare: :load_config do
|
40
|
-
|
67
|
+
admin_connection do
|
68
|
+
Rake::Task['db:prepare:original'].invoke
|
69
|
+
end
|
41
70
|
end
|
42
71
|
|
43
72
|
override_task setup: :load_config do
|
44
|
-
|
73
|
+
admin_connection do
|
74
|
+
Rake::Task['db:setup:original'].invoke
|
75
|
+
end
|
45
76
|
end
|
46
77
|
|
47
78
|
override_task prepare: :load_config do
|
48
|
-
|
79
|
+
admin_connection do
|
80
|
+
Rake::Task['db:reset:original'].invoke
|
81
|
+
end
|
49
82
|
end
|
50
83
|
|
51
84
|
override_task purge: :load_config do
|
52
|
-
|
85
|
+
admin_connection do
|
86
|
+
Rake::Task['db:purge:original'].invoke
|
87
|
+
end
|
53
88
|
end
|
54
89
|
|
55
90
|
override_task abort_if_pending_migrations: :load_config do
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
override_task seed: :load_config do
|
60
|
-
system('AS_DB_ADMIN=true rake db:seed:original')
|
91
|
+
admin_connection do
|
92
|
+
Rake::Task['db:abort_if_pending_migrations:original'].invoke
|
93
|
+
end
|
61
94
|
end
|
62
95
|
|
63
96
|
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
|
+
override_task grant_usage: :load_config do
|
110
|
+
admin_connection_test_db do
|
111
|
+
create_rls_user
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
64
115
|
override_task create: :load_config do
|
65
|
-
|
116
|
+
admin_connection_test_db do
|
117
|
+
Rake::Task['db:test:create:original'].invoke
|
118
|
+
end
|
66
119
|
end
|
67
120
|
|
68
121
|
override_task drop: :load_config do
|
69
|
-
|
122
|
+
admin_connection_test_db do
|
123
|
+
Rake::Task['db:test:drop:original'].invoke
|
124
|
+
end
|
70
125
|
end
|
71
126
|
|
72
127
|
override_task prepare: :load_config do
|
73
|
-
|
128
|
+
admin_connection_test_db do
|
129
|
+
Rake::Task['db:test:prepare:original'].invoke
|
130
|
+
end
|
74
131
|
end
|
75
132
|
|
76
133
|
override_task setup: :load_config do
|
77
|
-
|
134
|
+
admin_connection_test_db do
|
135
|
+
Rake::Task['db:test:setup:original'].invoke
|
136
|
+
end
|
78
137
|
end
|
79
138
|
|
80
139
|
override_task purge: :load_config do
|
81
|
-
|
140
|
+
admin_connection_test_db do
|
141
|
+
Rake::Task['db:test:purge:original'].invoke
|
142
|
+
end
|
82
143
|
end
|
83
144
|
|
84
145
|
override_task load_schema: :load_config do
|
85
|
-
|
146
|
+
admin_connection_test_db do
|
147
|
+
Rake::Task['db:test:load_schema:original'].invoke
|
148
|
+
end
|
86
149
|
end
|
87
150
|
end
|
88
151
|
|
89
152
|
namespace :enviroment do
|
90
153
|
override_task set: :load_config do
|
91
|
-
|
154
|
+
admin_connection do
|
155
|
+
Rake::Task['db:enviroment:set:original'].invoke
|
156
|
+
end
|
92
157
|
end
|
93
158
|
end
|
94
159
|
|
95
160
|
namespace :schema do
|
96
161
|
override_task load: :load_config do
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
162
|
+
admin_connection do
|
163
|
+
Rake::Task['db:schema:load:original'].invoke
|
164
|
+
Rake::Task['db:grant_usage'].invoke
|
165
|
+
Rake::Task['db:test:grant_usage'].invoke
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
override_task dump: :load_config do
|
170
|
+
admin_connection do
|
171
|
+
Rake::Task['db:schema:dump:original'].invoke
|
114
172
|
end
|
115
173
|
end
|
116
174
|
end
|
data/lib/pg_rls/multi_tenancy.rb
CHANGED
@@ -12,9 +12,17 @@ module PgRls
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def switch_tenant
|
15
|
-
Tenant.switch
|
15
|
+
Tenant.switch!(request.subdomain)
|
16
|
+
session[:_tenant] = request.subdomain
|
17
|
+
rescue PgRls::Errors::TenantNotFound, ActiveRecord::RecordNotFound
|
18
|
+
Tenant.switch(session[:_tenant])
|
16
19
|
rescue NoMethodError
|
20
|
+
session[:tenant] = nil
|
17
21
|
redirect_to '/'
|
18
22
|
end
|
23
|
+
|
24
|
+
def tenant_match_session_cookies?
|
25
|
+
session[:_tenant] == request.subdomain
|
26
|
+
end
|
19
27
|
end
|
20
28
|
end
|
@@ -6,11 +6,11 @@ module PgRls
|
|
6
6
|
module DownStatements
|
7
7
|
def drop_rls_user
|
8
8
|
ActiveRecord::Migration.execute <<~SQL
|
9
|
-
DROP OWNED BY #{PgRls
|
10
|
-
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM #{PgRls
|
11
|
-
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM #{PgRls
|
12
|
-
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM #{PgRls
|
13
|
-
DROP USER #{PgRls
|
9
|
+
DROP OWNED BY #{PgRls.username};
|
10
|
+
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM #{PgRls.username};
|
11
|
+
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM #{PgRls.username};
|
12
|
+
REVOKE ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public FROM #{PgRls.username};
|
13
|
+
DROP USER #{PgRls.username};
|
14
14
|
SQL
|
15
15
|
end
|
16
16
|
|
@@ -45,7 +45,7 @@ module PgRls
|
|
45
45
|
|
46
46
|
def drop_rls_policy(table_name)
|
47
47
|
ActiveRecord::Migration.execute <<-SQL
|
48
|
-
DROP POLICY #{table_name}_#{PgRls
|
48
|
+
DROP POLICY #{table_name}_#{PgRls.username} ON #{table_name};
|
49
49
|
ALTER TABLE #{table_name} DISABLE ROW LEVEL SECURITY;
|
50
50
|
SQL
|
51
51
|
end
|
@@ -11,7 +11,7 @@ module PgRls
|
|
11
11
|
include DownStatements
|
12
12
|
|
13
13
|
def create_rls_tenant_table(table_name, **options, &block)
|
14
|
-
create_rls_user
|
14
|
+
create_rls_user
|
15
15
|
create_rls_setter_function
|
16
16
|
create_rls_blocking_function
|
17
17
|
create_table(table_name, **options, &block)
|
@@ -41,7 +41,7 @@ module PgRls
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def convert_to_rls_tenant_table(table_name, **_options)
|
44
|
-
create_rls_user
|
44
|
+
create_rls_user
|
45
45
|
create_rls_setter_function
|
46
46
|
create_rls_blocking_function
|
47
47
|
add_rls_column_to_tenant_table(table_name)
|
@@ -4,29 +4,32 @@ module PgRls
|
|
4
4
|
module Schema
|
5
5
|
# Up Schema Statements
|
6
6
|
module UpStatements
|
7
|
-
def create_rls_user(name: PgRls
|
7
|
+
def create_rls_user(name: PgRls.username, password: PgRls.password, schema: 'public')
|
8
8
|
PgRls.execute <<-SQL
|
9
9
|
DO
|
10
10
|
$do$
|
11
11
|
BEGIN
|
12
12
|
IF NOT EXISTS (
|
13
|
-
|
14
|
-
|
13
|
+
SELECT FROM pg_catalog.pg_roles -- SELECT list can be empty for this
|
14
|
+
WHERE rolname = '#{name}') THEN
|
15
15
|
|
16
|
-
|
17
|
-
GRANT ALL PRIVILEGES ON TABLE schema_migrations TO #{name};
|
18
|
-
GRANT USAGE ON SCHEMA public TO #{name};
|
19
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
20
|
-
GRANT SELECT, INSERT, UPDATE, DELETE
|
21
|
-
ON TABLES TO #{name};
|
22
|
-
GRANT SELECT, INSERT, UPDATE, DELETE
|
23
|
-
ON ALL TABLES IN SCHEMA public
|
24
|
-
TO #{name};
|
25
|
-
GRANT USAGE, SELECT
|
26
|
-
ON ALL SEQUENCES IN SCHEMA public
|
27
|
-
TO #{name};
|
16
|
+
CREATE USER #{name} WITH PASSWORD '#{password}';
|
28
17
|
END IF;
|
29
|
-
|
18
|
+
GRANT ALL PRIVILEGES ON TABLE schema_migrations TO #{name};
|
19
|
+
GRANT USAGE ON SCHEMA #{schema} TO #{name};
|
20
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
21
|
+
GRANT USAGE, SELECT
|
22
|
+
ON SEQUENCES TO #{name};
|
23
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema}
|
24
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
25
|
+
ON TABLES TO #{name};
|
26
|
+
GRANT SELECT, INSERT, UPDATE, DELETE
|
27
|
+
ON ALL TABLES IN SCHEMA #{schema}
|
28
|
+
TO #{name};
|
29
|
+
GRANT USAGE, SELECT
|
30
|
+
ON ALL SEQUENCES IN SCHEMA #{schema}
|
31
|
+
TO #{name};
|
32
|
+
END;
|
30
33
|
$do$;
|
31
34
|
SQL
|
32
35
|
end
|
@@ -87,7 +90,7 @@ module PgRls
|
|
87
90
|
SQL
|
88
91
|
end
|
89
92
|
|
90
|
-
def create_rls_policy(table_name, user = PgRls
|
93
|
+
def create_rls_policy(table_name, user = PgRls.username)
|
91
94
|
ActiveRecord::Migration.execute <<-SQL
|
92
95
|
ALTER TABLE #{table_name} ENABLE ROW LEVEL SECURITY;
|
93
96
|
CREATE POLICY #{table_name}_#{user}
|
@@ -3,24 +3,23 @@
|
|
3
3
|
module PgRls
|
4
4
|
# Ensure Connection is with App_use
|
5
5
|
module SecureConnection
|
6
|
-
def self.
|
7
|
-
|
8
|
-
after_initialize :establish_secure_connection
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
6
|
+
def self.establish_secure_connection
|
7
|
+
return if secure_connection_established?
|
13
8
|
|
14
|
-
def establish_secure_connection
|
15
9
|
return if PgRls.default_connection?
|
16
10
|
|
17
|
-
return if secure_connection_established?
|
18
|
-
|
19
11
|
PgRls.establish_new_connection
|
20
12
|
end
|
21
13
|
|
22
|
-
def secure_connection_established?
|
23
|
-
PgRls.current_connection_username == PgRls
|
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.class_eval do
|
21
|
+
# after_initialize :establish_secure_connection
|
22
|
+
# end
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
data/lib/pg_rls/tenant.rb
CHANGED
@@ -4,19 +4,21 @@ module PgRls
|
|
4
4
|
# Tenant Controller
|
5
5
|
module Tenant
|
6
6
|
class << self
|
7
|
+
attr_reader :tenant
|
8
|
+
|
7
9
|
def switch(resource)
|
8
|
-
|
9
|
-
connection_adapter = PgRls.connection_class
|
10
|
-
find_tenant(resource)
|
11
|
-
connection_adapter.connection.execute(format('SET rls.tenant_id = %s',
|
12
|
-
connection_adapter.connection.quote(tenant.tenant_id)))
|
13
|
-
"RLS changed to '#{tenant.send(@method)}'"
|
10
|
+
switch_tenant!(resource)
|
14
11
|
rescue StandardError => e
|
15
12
|
puts 'connection was not made'
|
16
|
-
puts
|
13
|
+
puts e
|
17
14
|
end
|
18
15
|
|
19
|
-
|
16
|
+
def switch!(resource)
|
17
|
+
switch_tenant!(resource)
|
18
|
+
rescue StandardError => e
|
19
|
+
puts 'connection was not made'
|
20
|
+
raise e
|
21
|
+
end
|
20
22
|
|
21
23
|
def fetch
|
22
24
|
@fetch ||= PgRls.main_model.find_by_tenant_id(
|
@@ -28,16 +30,28 @@ module PgRls
|
|
28
30
|
'no tenant is selected'
|
29
31
|
end
|
30
32
|
|
33
|
+
private
|
34
|
+
|
35
|
+
def switch_tenant!(resource)
|
36
|
+
@fetch = nil
|
37
|
+
connection_adapter = PgRls.connection_class
|
38
|
+
find_tenant(resource)
|
39
|
+
|
40
|
+
raise PgRls::Errors::TenantNotFound unless tenant.present?
|
41
|
+
|
42
|
+
connection_adapter.connection.execute(format('SET rls.tenant_id = %s',
|
43
|
+
connection_adapter.connection.quote(tenant.tenant_id)))
|
44
|
+
"RLS changed to '#{tenant.send(@method)}'"
|
45
|
+
end
|
46
|
+
|
31
47
|
def find_tenant(resource)
|
32
48
|
@tenant = nil
|
33
49
|
|
34
50
|
PgRls.search_methods.each do |method|
|
35
51
|
@method = method
|
36
52
|
@tenant ||= PgRls.main_model.send("find_by_#{method}!", resource)
|
37
|
-
rescue NoMethodError => e
|
53
|
+
rescue NoMethodError, ActiveRecord::RecordNotFound => e
|
38
54
|
@error = e
|
39
|
-
rescue ActiveRecord::RecordNotFound
|
40
|
-
raise PgRls::Errors::TenantNotFound
|
41
55
|
end
|
42
56
|
end
|
43
57
|
end
|
data/lib/pg_rls/version.rb
CHANGED
data/lib/pg_rls.rb
CHANGED
@@ -14,7 +14,7 @@ require_relative 'pg_rls/errors/tenant_not_found'
|
|
14
14
|
# PostgreSQL Row Level Security
|
15
15
|
module PgRls
|
16
16
|
class Error < StandardError; end
|
17
|
-
SECURE_USERNAME =
|
17
|
+
SECURE_USERNAME = 'app_user'
|
18
18
|
|
19
19
|
class << self
|
20
20
|
extend Forwardable
|
@@ -32,7 +32,7 @@ module PgRls
|
|
32
32
|
attr_reader(*READER_METHODS)
|
33
33
|
|
34
34
|
def_delegators(*DELEGATORS_METHODS)
|
35
|
-
|
35
|
+
|
36
36
|
def setup
|
37
37
|
yield self
|
38
38
|
end
|
@@ -54,13 +54,13 @@ module PgRls
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def admin_execute(query = nil)
|
57
|
-
self.
|
57
|
+
self.as_db_admin = true
|
58
58
|
establish_new_connection
|
59
59
|
return yield if block_given?
|
60
60
|
|
61
61
|
execute(query)
|
62
62
|
ensure
|
63
|
-
self.
|
63
|
+
self.as_db_admin = false
|
64
64
|
establish_new_connection
|
65
65
|
end
|
66
66
|
|
@@ -70,7 +70,7 @@ module PgRls
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def default_connection?
|
73
|
-
|
73
|
+
as_db_admin
|
74
74
|
end
|
75
75
|
|
76
76
|
def main_model
|
@@ -96,20 +96,46 @@ module PgRls
|
|
96
96
|
|
97
97
|
def database_default_configuration
|
98
98
|
connection_class.connection.pool.db_config.configuration_hash
|
99
|
+
rescue ActiveRecord::NoDatabaseError
|
100
|
+
connection_class.connection_db_config.configuration_hash
|
101
|
+
end
|
102
|
+
|
103
|
+
def database_admin_configuration
|
104
|
+
enviroment_db_configuration = database_connection_file[Rails.env]
|
105
|
+
|
106
|
+
return enviroment_db_configuration if enviroment_db_configuration['username'].present?
|
107
|
+
|
108
|
+
enviroment_db_configuration.first.last
|
99
109
|
end
|
100
110
|
|
101
111
|
def database_configuration
|
112
|
+
return database_admin_configuration if default_connection?
|
113
|
+
|
102
114
|
current_configuration = database_default_configuration.deep_dup
|
103
|
-
current_configuration.tap
|
115
|
+
current_configuration.tap do |config|
|
116
|
+
config[:username] = PgRls.username
|
117
|
+
config[:password] = PgRls.password
|
118
|
+
end
|
119
|
+
|
104
120
|
current_configuration.freeze
|
105
121
|
end
|
106
122
|
end
|
123
|
+
|
124
|
+
mattr_accessor :as_db_admin
|
125
|
+
@@as_db_admin = false
|
126
|
+
|
107
127
|
mattr_accessor :table_name
|
108
128
|
@@table_name = 'companies'
|
109
129
|
|
110
130
|
mattr_accessor :class_name
|
111
131
|
@@class_name = 'Company'
|
112
132
|
|
133
|
+
mattr_accessor :username
|
134
|
+
@@username = 'app_user'
|
135
|
+
|
136
|
+
mattr_accessor :password
|
137
|
+
@@password = 'password'
|
138
|
+
|
113
139
|
mattr_accessor :search_methods
|
114
140
|
@@search_methods = %i[subdomain id tenant_id]
|
115
141
|
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.0.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: 2022-
|
11
|
+
date: 2022-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -92,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0'
|
94
94
|
requirements: []
|
95
|
-
rubygems_version: 3.3.
|
95
|
+
rubygems_version: 3.3.15
|
96
96
|
signing_key:
|
97
97
|
specification_version: 4
|
98
98
|
summary: Write a short summary, because RubyGems requires one.
|