granar 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +53 -0
- data/Rakefile +8 -0
- data/app/assets/config/rbac_rls_manifest.js +2 -0
- data/app/assets/javascripts/rbac_rls/application.js +1 -0
- data/app/assets/stylesheets/rbac_rls/application.css.scss +16 -0
- data/app/controllers/concerns/connection_rls_user_concern.rb +31 -0
- data/app/controllers/rbac_rls/application_controller.rb +3 -0
- data/app/controllers/rbac_rls/groups_controller.rb +94 -0
- data/app/controllers/rbac_rls/home_controller.rb +5 -0
- data/app/controllers/rbac_rls/permissions_controller.rb +69 -0
- data/app/controllers/rbac_rls/roles_controller.rb +61 -0
- data/app/helpers/rbac_rls/application_helper.rb +4 -0
- data/app/helpers/rbac_rls/groups_helper.rb +4 -0
- data/app/helpers/rbac_rls/permissions_helper.rb +41 -0
- data/app/helpers/rbac_rls/roles_helper.rb +4 -0
- data/app/jobs/rbac_rls/application_job.rb +4 -0
- data/app/mailers/rbac_rls/application_mailer.rb +6 -0
- data/app/models/concerns/connection_rls_concern.rb +19 -0
- data/app/models/rbac_rls/application_record.rb +3 -0
- data/app/models/rbac_rls/group.rb +14 -0
- data/app/models/rbac_rls/group_permission.rb +20 -0
- data/app/models/rbac_rls/group_user.rb +7 -0
- data/app/models/rbac_rls/permission.rb +68 -0
- data/app/models/rbac_rls/role.rb +10 -0
- data/app/models/rbac_rls/role_permission.rb +6 -0
- data/app/models/rbac_rls/user_role.rb +7 -0
- data/app/views/layouts/rbac_rls/application.html.erb +55 -0
- data/app/views/rbac_rls/groups/_form.html.erb +97 -0
- data/app/views/rbac_rls/groups/_group.html.erb +12 -0
- data/app/views/rbac_rls/groups/_group_permission_fields.html.erb +18 -0
- data/app/views/rbac_rls/groups/_group_user_fields.html.erb +9 -0
- data/app/views/rbac_rls/groups/edit.html.erb +10 -0
- data/app/views/rbac_rls/groups/index.html.erb +14 -0
- data/app/views/rbac_rls/groups/new.html.erb +9 -0
- data/app/views/rbac_rls/groups/show.html.erb +10 -0
- data/app/views/rbac_rls/home/_link_to_home_page.html.erb +3 -0
- data/app/views/rbac_rls/home/index.html.erb +28 -0
- data/app/views/rbac_rls/permissions/_form.html.erb +78 -0
- data/app/views/rbac_rls/permissions/_permission.html.erb +54 -0
- data/app/views/rbac_rls/permissions/_role_permission_fields.html.erb +9 -0
- data/app/views/rbac_rls/permissions/edit.html.erb +7 -0
- data/app/views/rbac_rls/permissions/index.html.erb +18 -0
- data/app/views/rbac_rls/permissions/new.html.erb +9 -0
- data/app/views/rbac_rls/permissions/show.html.erb +10 -0
- data/app/views/rbac_rls/roles/_form.html.erb +42 -0
- data/app/views/rbac_rls/roles/_role.html.erb +2 -0
- data/app/views/rbac_rls/roles/_user_role_fields.html.erb +9 -0
- data/app/views/rbac_rls/roles/edit.html.erb +9 -0
- data/app/views/rbac_rls/roles/index.html.erb +19 -0
- data/app/views/rbac_rls/roles/new.html.erb +8 -0
- data/app/views/rbac_rls/roles/show.html.erb +12 -0
- data/config/assets.rb +12 -0
- data/config/importmap.rb +9 -0
- data/config/routes.rb +14 -0
- data/config/setup.rb +0 -0
- data/db/migrate/20220411125339_create_rbac_rls_roles.rb +9 -0
- data/db/migrate/20220411125613_create_rbac_rls_user_roles.rb +9 -0
- data/db/migrate/20220411133054_create_rbac_rls_permissions.rb +18 -0
- data/db/migrate/20220425212731_create_role_permissions.rb +9 -0
- data/db/migrate/20220912104712_create_rbac_rls_groups.rb +10 -0
- data/db/migrate/20220912104929_create_rbac_rls_group_permissions.rb +12 -0
- data/db/migrate/20220914004802_create_rbac_rls_group_users.rb +10 -0
- data/db/migrate/20220914004803_create_basic_permissions_for_application_acess.rb +18 -0
- data/lib/generators/generator_helpers.rb +8 -0
- data/lib/generators/rbac_rls/custom_migration_generator.rb +78 -0
- data/lib/generators/rbac_rls/group_permission_generator.rb +57 -0
- data/lib/generators/rbac_rls/templates/group_permission_migration.rb +83 -0
- data/lib/generators/rbac_rls/templates/rls_migration.rb +81 -0
- data/lib/generators/rbac_rls/templates/rls_migration.rb.erb +64 -0
- data/lib/generators/rbac_rls/templates/rls_migration2.rb.erb +80 -0
- data/lib/rbac_rls/engine.rb +21 -0
- data/lib/rbac_rls/version.rb +3 -0
- data/lib/rbac_rls.rb +6 -0
- data/lib/tasks/rbac_rls_tasks.rake +4 -0
- metadata +178 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateRbacRlsPermissions < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :permissions do |t|
|
4
|
+
t.string :name, limit: 30
|
5
|
+
t.string :table_name, limit: 255, null: false
|
6
|
+
t.boolean :read, default: false
|
7
|
+
t.boolean :write, default: false
|
8
|
+
t.boolean :change, default: false
|
9
|
+
t.boolean :remove, default: false
|
10
|
+
t.boolean :owner_read, default: false
|
11
|
+
t.boolean :owner_change, default: false
|
12
|
+
t.boolean :owner_remove, default: false
|
13
|
+
t.references :permission, null: true, index: true
|
14
|
+
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateRbacRlsGroupPermissions < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :group_permissions do |t|
|
4
|
+
t.references :permission, null: false, foreign_key: true
|
5
|
+
t.references :group, null: false, foreign_key: true
|
6
|
+
t.string :table_key, null: false, limit: 60
|
7
|
+
t.string :table_value, null: false, limit: 60
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateBasicPermissionsForApplicationAcess < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
schema = 'public'
|
4
|
+
user = 'app_user'
|
5
|
+
where_schema = ->(schema_name = nil) do
|
6
|
+
schema_name.present? ? "WHERE table_schema = '#{schema_name}'" : ''
|
7
|
+
end
|
8
|
+
|
9
|
+
sql = "SELECT table_name FROM information_schema.tables #{where_schema[schema]}"
|
10
|
+
result = ActiveRecord::Base.connection.select_all(sql)
|
11
|
+
tables = result.map { |k| k['table_name'] }
|
12
|
+
tables&.each do |table|
|
13
|
+
execute "GRANT ALL PRIVILEGES on #{schema}.#{table} TO #{user}"
|
14
|
+
end
|
15
|
+
|
16
|
+
execute 'GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# class_option :skip_show, type: :boolean, default: false, desc: 'Skip "show" action'
|
2
|
+
# class_option :attributes, type: :hash, default: {}
|
3
|
+
# require 'rails/generators'
|
4
|
+
# exemplo: # rails generate rbac_rls:custom_migration products
|
5
|
+
|
6
|
+
require 'rails/command/base'
|
7
|
+
require 'rails/generators/named_base'
|
8
|
+
require 'generators/generator_helpers'
|
9
|
+
require 'rails/generators/rails/migration/migration_generator'
|
10
|
+
require 'rails/generators/migration'
|
11
|
+
require 'active_record/migration'
|
12
|
+
require 'thor/actions/inject_into_file'
|
13
|
+
module Rails
|
14
|
+
module Generators
|
15
|
+
module Migration
|
16
|
+
module ClassMethods
|
17
|
+
def next_migration_number(dirname)
|
18
|
+
next_migration_number = current_migration_number(dirname) + 1
|
19
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
#deve se sobrescrever os tipos pois será criado um novo tipo para o row level security
|
26
|
+
# ActiveRecord::Base.connection.native_database_types
|
27
|
+
ActiveRecord::Base.connection.class_eval do
|
28
|
+
def native_database_types # :nodoc:
|
29
|
+
{ role: { name: "rls" },
|
30
|
+
all: { name: "rls" },
|
31
|
+
user: { name: "rls" }, }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
module RbacRls
|
37
|
+
module Generators
|
38
|
+
class CustomMigrationGenerator < Rails::Generators::NamedBase
|
39
|
+
attr_accessor :time_now
|
40
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
41
|
+
|
42
|
+
class_option :permission_identifier, type: :string, default: Time.now.getutc.to_i.to_s, desc: ''
|
43
|
+
# class_option :sufix_migration_name, type: :string, default: Time.now.getutc.to_i.to_s, desc: ''
|
44
|
+
include Rails::Generators::Migration
|
45
|
+
include RbacRls::Generators::GeneratorHelpers
|
46
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements
|
47
|
+
# include Rails::Generators::MigrationGenerator
|
48
|
+
# ActiveRecord::ConnectionAdapters::Table
|
49
|
+
# Rails::Generators::Error
|
50
|
+
# ActiveRecord::Base.connection.valid_type?
|
51
|
+
|
52
|
+
source_root File.expand_path('../templates', __FILE__)
|
53
|
+
|
54
|
+
def migration_define_template
|
55
|
+
attrs = attributes.map { |i| i.name }
|
56
|
+
str = attrs.join('_')
|
57
|
+
|
58
|
+
migration_template('../templates/rls_migration.rb',
|
59
|
+
File.join('db/migrate', "create_#{name.underscore}_#{options[:permission_identifier]}_policy.rb")
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# def insert_code
|
68
|
+
# insert_into_file 'app/controllers/application_controller.rb', :before => "end" do
|
69
|
+
# "\n around_action :with_customer_id \n"
|
70
|
+
# end
|
71
|
+
# insert_into_file 'app/controllers/application_controller.rb', :before => "end" do
|
72
|
+
# "\ndef with_customer_id
|
73
|
+
# ApplicationRecord.with_customer_id(current_user.id) do
|
74
|
+
# yield
|
75
|
+
# end
|
76
|
+
# end\n"
|
77
|
+
# end
|
78
|
+
# end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# exemplo: # rails generate rbac_rls:custom_migration products
|
2
|
+
|
3
|
+
require 'rails/command/base'
|
4
|
+
require 'rails/generators/named_base'
|
5
|
+
require 'generators/generator_helpers'
|
6
|
+
require 'rails/generators/rails/migration/migration_generator'
|
7
|
+
require 'rails/generators/migration'
|
8
|
+
require 'active_record/migration'
|
9
|
+
require 'thor/actions/inject_into_file'
|
10
|
+
module Rails
|
11
|
+
module Generators
|
12
|
+
module Migration
|
13
|
+
module ClassMethods
|
14
|
+
def next_migration_number(dirname)
|
15
|
+
next_migration_number = current_migration_number(dirname) + 1
|
16
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
#deve se sobrescrever os tipos pois será criado um novo tipo para o row level security
|
24
|
+
# ActiveRecord::Base.connection.native_database_types
|
25
|
+
ActiveRecord::Base.connection.class_eval do
|
26
|
+
def native_database_types # :nodoc:
|
27
|
+
{ table_key: { name: "rls" },
|
28
|
+
table_value: { name: "rls" },
|
29
|
+
user: { name: "rls" }, }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
module RbacRls
|
35
|
+
module Generators
|
36
|
+
class GroupPermissionGenerator < Rails::Generators::NamedBase
|
37
|
+
attr_accessor :time_now
|
38
|
+
argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
|
39
|
+
class_option :identifier, type: :string, default: Time.now.getutc.to_i.to_s, desc: ''
|
40
|
+
include Rails::Generators::Migration
|
41
|
+
include RbacRls::Generators::GeneratorHelpers
|
42
|
+
|
43
|
+
source_root File.expand_path('../templates', __FILE__)
|
44
|
+
|
45
|
+
def migration_define_template
|
46
|
+
attrs = attributes.map { |i| i.name }
|
47
|
+
str = attrs.join('_')
|
48
|
+
|
49
|
+
full_name = "#{name}#{options[:identifier]}".underscore
|
50
|
+
# byebug
|
51
|
+
migration_template('../templates/group_permission_migration.rb',
|
52
|
+
File.join('db/migrate', "create_#{full_name}_gpolicy.rb"))
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
<%
|
2
|
+
# rails generate rbac_rls:custom_migration table_name
|
3
|
+
time_now = Time.now.getutc.to_i
|
4
|
+
attrs = attributes.map { |i| i.name.camelize }
|
5
|
+
table_key = attributes.select{ |attr| attr.type.to_sym == :table_key }.first.name
|
6
|
+
table_value = attributes.select{ |attr| attr.type.to_sym == :table_value }.first.name
|
7
|
+
gen_table_name = name.underscore
|
8
|
+
|
9
|
+
limit_policy_name = 63
|
10
|
+
type_polices = {insert: :write,
|
11
|
+
select: :read,
|
12
|
+
update: :change,
|
13
|
+
delete: :remove}
|
14
|
+
type_polices_owner = {select: :owner_read,
|
15
|
+
update: :owner_change,
|
16
|
+
delete: :owner_remove}
|
17
|
+
# byebug
|
18
|
+
%>
|
19
|
+
class Create<%= "#{"#{name}#{options[:identifier]}".camelize}" %>Gpolicy < ActiveRecord::Migration[7.0]
|
20
|
+
|
21
|
+
def change
|
22
|
+
<%
|
23
|
+
policy_name = ->(table_name,method) { "p#{time_now}#{table_name}#{method}"[0,limit_policy_name] }
|
24
|
+
db_role = "app_user"
|
25
|
+
permission_id = options[:permission_identifier]
|
26
|
+
owner_rls_policy = ->(type){
|
27
|
+
if type.to_sym != :insert
|
28
|
+
"
|
29
|
+
or
|
30
|
+
(
|
31
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
32
|
+
(
|
33
|
+
SELECT
|
34
|
+
gu.user_id
|
35
|
+
FROM permissions p
|
36
|
+
INNER JOIN group_permissions gp on gp.permission_id = p.id
|
37
|
+
INNER JOIN group_users gu on gu.group_id = gp.group_id
|
38
|
+
WHERE (p.\"#{type_polices[type.to_sym]}\")
|
39
|
+
AND p.table_name = '#{gen_table_name}'
|
40
|
+
) and owner_id = NULLIF(current_setting('rls.user_id', TRUE), '')::bigint )
|
41
|
+
"
|
42
|
+
end
|
43
|
+
}
|
44
|
+
constraint_for_rls = ->(type) {"
|
45
|
+
((
|
46
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
47
|
+
(
|
48
|
+
SELECT
|
49
|
+
gu.user_id
|
50
|
+
FROM permissions p
|
51
|
+
INNER JOIN group_permissions gp on gp.permission_id = p.id
|
52
|
+
INNER JOIN group_users gu on gu.group_id = gp.group_id
|
53
|
+
WHERE (p.\"#{type_polices[type.to_sym]}\")
|
54
|
+
AND p.table_name = '#{gen_table_name}'
|
55
|
+
)
|
56
|
+
) #{owner_rls_policy[type]} ) and #{table_key.to_s} LIKE '%#{table_value}%'
|
57
|
+
|
58
|
+
"
|
59
|
+
}
|
60
|
+
%>
|
61
|
+
|
62
|
+
<% %i(insert select update delete).each do |attr| %>
|
63
|
+
execute '<%="GRANT #{attr} ON #{name} TO #{db_role}"%>'
|
64
|
+
execute '<%= "ALTER TABLE #{name} ENABLE ROW LEVEL SECURITY" %>'
|
65
|
+
reversible do |dir|
|
66
|
+
dir.up do
|
67
|
+
<%if attr.to_sym ==:insert %>
|
68
|
+
execute <<-SQL
|
69
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} with check (#{constraint_for_rls[attr.to_s]})" %>
|
70
|
+
SQL
|
71
|
+
<% elsif %i(select update delete).include?(attr.to_sym) %>
|
72
|
+
execute <<-SQL
|
73
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} using (#{constraint_for_rls[attr.to_s]})" %>
|
74
|
+
SQL
|
75
|
+
<%end%>
|
76
|
+
end
|
77
|
+
end
|
78
|
+
<% end %>
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
<%
|
2
|
+
# rails generate rbac_rls:custom_migration table_name
|
3
|
+
time_now = Time.now.getutc.to_i
|
4
|
+
attrs = attributes.map { |i| i.name.camelize }
|
5
|
+
|
6
|
+
limit_policy_name = 63
|
7
|
+
type_polices = {insert: :write,
|
8
|
+
select: :read,
|
9
|
+
update: :change,
|
10
|
+
delete: :remove}
|
11
|
+
type_polices_owner = {select: :owner_read,
|
12
|
+
update: :owner_change,
|
13
|
+
delete: :owner_remove}
|
14
|
+
%>
|
15
|
+
|
16
|
+
class Create<%= "#{name.underscore.camelize}#{options[:permission_identifier]}" %>Policy < ActiveRecord::Migration[7.0]
|
17
|
+
|
18
|
+
def change
|
19
|
+
<%
|
20
|
+
policy_name = ->(table_name,method) { "p#{time_now}#{table_name}#{method}"[0,limit_policy_name] }
|
21
|
+
db_role = "app_user"
|
22
|
+
permission_id = options[:permission_identifier]
|
23
|
+
owner_rls_policy = ->(type){
|
24
|
+
if type.to_sym != :insert
|
25
|
+
"
|
26
|
+
or
|
27
|
+
(
|
28
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
29
|
+
(
|
30
|
+
SELECT
|
31
|
+
ur.user_id
|
32
|
+
FROM permissions p
|
33
|
+
INNER JOIN role_permissions rp on rp.permission_id = p.id
|
34
|
+
INNER JOIN user_roles ur on rp.role_id = ur.role_id
|
35
|
+
WHERE (p.\"#{type_polices_owner[type.to_sym]}\")
|
36
|
+
AND p.table_name = 'products'
|
37
|
+
) and owner_id = NULLIF(current_setting('rls.user_id', TRUE), '')::bigint )
|
38
|
+
"
|
39
|
+
end
|
40
|
+
}
|
41
|
+
constraint_for_rls = ->(type) {"
|
42
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
43
|
+
(
|
44
|
+
SELECT
|
45
|
+
ur.user_id
|
46
|
+
FROM permissions p
|
47
|
+
INNER JOIN role_permissions rp on rp.permission_id = p.id
|
48
|
+
INNER JOIN user_roles ur on rp.role_id = ur.role_id
|
49
|
+
WHERE (p.\"#{type_polices[type.to_sym]}\")
|
50
|
+
AND p.table_name = 'products'
|
51
|
+
)
|
52
|
+
#{owner_rls_policy[type]}
|
53
|
+
"
|
54
|
+
}
|
55
|
+
%>
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
<% %i(insert select update delete).each do |attr| %>
|
61
|
+
execute '<%="GRANT #{attr} ON #{name} TO #{db_role}"%>'
|
62
|
+
execute '<%= "ALTER TABLE #{name} ENABLE ROW LEVEL SECURITY" %>'
|
63
|
+
reversible do |dir|
|
64
|
+
dir.up do
|
65
|
+
<%if attr.to_sym ==:insert %>
|
66
|
+
execute <<-SQL
|
67
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} with check (#{constraint_for_rls[attr.to_s]})" %>
|
68
|
+
SQL
|
69
|
+
<% elsif %i(select update delete).include?(attr.to_sym) %>
|
70
|
+
execute <<-SQL
|
71
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} using (#{constraint_for_rls[attr.to_s]})" %>
|
72
|
+
SQL
|
73
|
+
<%end%>
|
74
|
+
end
|
75
|
+
end
|
76
|
+
<% end %>
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<%
|
2
|
+
time_now = Time.now.getutc.to_i
|
3
|
+
attrs = attributes.map { |i| i.name.camelize }
|
4
|
+
str = attrs.join('')
|
5
|
+
limit_policy_name = 63
|
6
|
+
type_polices = {insert: :write,select: :read,update: :change, delete: :remove}
|
7
|
+
type_polices_owner = {insert: :owner_write,select: :owner_read,update: :owner_change, delete: :owner_remove}
|
8
|
+
|
9
|
+
%>
|
10
|
+
|
11
|
+
class Create<%= "#{name.underscore.camelize}" %>Policy<%="#{}" %> < ActiveRecord::Migration[7.0]
|
12
|
+
|
13
|
+
def change
|
14
|
+
<%
|
15
|
+
policy_name = ->(table_name,method) { "p#{time_now}#{table_name}#{method}"[0,limit_policy_name] }
|
16
|
+
db_role = "app_user"
|
17
|
+
permission_id = options[:permission_identifier]
|
18
|
+
|
19
|
+
constraint_for_rls = ->(type) {"
|
20
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
21
|
+
(
|
22
|
+
SELECT
|
23
|
+
ur.user_id
|
24
|
+
FROM permissions p
|
25
|
+
INNER JOIN role_permissions rp on rp.permission_id = p.id
|
26
|
+
INNER JOIN user_roles ur on rp.role_id = ur.role_id
|
27
|
+
WHERE (p.\"#{type_polices[type.to_sym]}\")
|
28
|
+
AND p.table_name = 'products'
|
29
|
+
)
|
30
|
+
or
|
31
|
+
(
|
32
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
33
|
+
(
|
34
|
+
SELECT
|
35
|
+
ur.user_id
|
36
|
+
FROM permissions p
|
37
|
+
INNER JOIN role_permissions rp on rp.permission_id = p.id
|
38
|
+
INNER JOIN user_roles ur on rp.role_id = ur.role_id
|
39
|
+
WHERE (p.\"#{type_polices_owner[type.to_sym]}\")
|
40
|
+
AND p.table_name = 'products'
|
41
|
+
) and owner_id = NULLIF(current_setting('rls.user_id', TRUE), '')::bigint
|
42
|
+
)
|
43
|
+
"
|
44
|
+
}
|
45
|
+
%>
|
46
|
+
<% %i(insert select update delete).each do |attr| %>
|
47
|
+
execute '<%="GRANT #{attr} ON #{name} TO #{db_role}"%>'
|
48
|
+
execute '<%= "ALTER TABLE #{name} ENABLE ROW LEVEL SECURITY" %>'
|
49
|
+
reversible do |dir|
|
50
|
+
dir.up do
|
51
|
+
<%if attr.to_sym ==:insert %>
|
52
|
+
execute <<-SQL
|
53
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} with check (#{constraint_for_rls[attr.to_s]})" %>
|
54
|
+
SQL
|
55
|
+
<% elsif %i(select update delete).include?(attr.to_sym) %>
|
56
|
+
execute <<-SQL
|
57
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} using (#{constraint_for_rls[attr.to_s]})" %>
|
58
|
+
SQL
|
59
|
+
<%end%>
|
60
|
+
end
|
61
|
+
end
|
62
|
+
<% end %>
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
<%
|
2
|
+
time_now = Time.now.getutc.to_i
|
3
|
+
attrs = attributes.map { |i| i.name.camelize }
|
4
|
+
|
5
|
+
limit_policy_name = 63
|
6
|
+
type_polices = {insert: :write,
|
7
|
+
select: :read,
|
8
|
+
update: :change,
|
9
|
+
delete: :remove}
|
10
|
+
type_polices_owner = {select: :owner_read,
|
11
|
+
update: :owner_change,
|
12
|
+
delete: :owner_remove}
|
13
|
+
%>
|
14
|
+
|
15
|
+
class Create<%= "#{name.underscore.camelize}#{options[:permission_identifier]}" %>Policy < ActiveRecord::Migration[7.0]
|
16
|
+
|
17
|
+
def change
|
18
|
+
<%
|
19
|
+
policy_name = ->(table_name,method) { "p#{time_now}#{table_name}#{method}"[0,limit_policy_name] }
|
20
|
+
db_role = "app_user"
|
21
|
+
permission_id = options[:permission_identifier]
|
22
|
+
owner_rls_policy = ->(type){
|
23
|
+
if type.to_sym != :insert
|
24
|
+
"
|
25
|
+
or
|
26
|
+
(
|
27
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
28
|
+
(
|
29
|
+
SELECT
|
30
|
+
ur.user_id
|
31
|
+
FROM permissions p
|
32
|
+
INNER JOIN role_permissions rp on rp.permission_id = p.id
|
33
|
+
INNER JOIN user_roles ur on rp.role_id = ur.role_id
|
34
|
+
WHERE (p.\"#{type_polices_owner[type.to_sym]}\")
|
35
|
+
AND p.table_name = 'products'
|
36
|
+
) and owner_id = NULLIF(current_setting('rls.user_id', TRUE), '')::bigint )
|
37
|
+
"
|
38
|
+
end
|
39
|
+
}
|
40
|
+
constraint_for_rls = ->(type) {"
|
41
|
+
NULLIF(current_setting('rls.user_id', TRUE), '')::bigint in
|
42
|
+
(
|
43
|
+
SELECT
|
44
|
+
ur.user_id
|
45
|
+
FROM permissions p
|
46
|
+
INNER JOIN role_permissions rp on rp.permission_id = p.id
|
47
|
+
INNER JOIN user_roles ur on rp.role_id = ur.role_id
|
48
|
+
WHERE (p.\"#{type_polices[type.to_sym]}\")
|
49
|
+
AND p.table_name = 'products'
|
50
|
+
)
|
51
|
+
#{owner_rls_policy[type]}
|
52
|
+
"
|
53
|
+
}
|
54
|
+
%>
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
<% %i(insert select update delete).each do |attr| %>
|
60
|
+
execute '<%="GRANT #{attr} ON #{name} TO #{db_role}"%>'
|
61
|
+
execute '<%= "ALTER TABLE #{name} ENABLE ROW LEVEL SECURITY" %>'
|
62
|
+
reversible do |dir|
|
63
|
+
dir.up do
|
64
|
+
<%if attr.to_sym ==:insert %>
|
65
|
+
execute <<-SQL
|
66
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} with check (#{constraint_for_rls[attr.to_s]})" %>
|
67
|
+
SQL
|
68
|
+
<% elsif %i(select update delete).include?(attr.to_sym) %>
|
69
|
+
execute <<-SQL
|
70
|
+
<%= "CREATE POLICY #{policy_name.(name, attr.to_sym)} ON #{name} for #{attr.to_s} TO #{db_role} using (#{constraint_for_rls[attr.to_s]})" %>
|
71
|
+
SQL
|
72
|
+
<%end%>
|
73
|
+
end
|
74
|
+
end
|
75
|
+
<% end %>
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'importmap-rails'
|
2
|
+
require 'importmap/commands'
|
3
|
+
require 'importmap/map'
|
4
|
+
require 'importmap/packager'
|
5
|
+
require 'importmap/reloader'
|
6
|
+
|
7
|
+
require 'bootstrap'
|
8
|
+
require 'vanilla_nested'
|
9
|
+
require 'vanilla_nested/view_helpers'
|
10
|
+
|
11
|
+
module RbacRls
|
12
|
+
class Engine < ::Rails::Engine
|
13
|
+
isolate_namespace RbacRls
|
14
|
+
|
15
|
+
# initializer "rbac_rls.precompile" do |app|
|
16
|
+
# app.config.assets.paths << Rails.root.join('app/assets/javascripts')
|
17
|
+
# app.config.assets.precompile << "application.js"
|
18
|
+
# end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/rbac_rls.rb
ADDED