rabarber 6.0.2 → 6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -4
- data/README.md +49 -20
- data/app/models/rabarber/role.rb +153 -0
- data/app/models/rabarber/role_assignment.rb +10 -0
- data/lib/generators/rabarber/roles_generator.rb +6 -2
- data/lib/generators/rabarber/templates/create_rabarber_roles.rb.erb +4 -4
- data/lib/rabarber/configuration.rb +4 -4
- data/lib/rabarber/controllers/concerns/authorization.rb +5 -24
- data/lib/rabarber/core/cache.rb +19 -9
- data/lib/rabarber/core/roleable.rb +3 -5
- data/lib/rabarber/core/rule.rb +7 -13
- data/lib/rabarber/engine.rb +31 -0
- data/lib/rabarber/helpers/migration_helpers.rb +5 -16
- data/lib/rabarber/inputs/base.rb +5 -2
- data/lib/rabarber/inputs/boolean.rb +3 -1
- data/lib/rabarber/inputs/context.rb +5 -6
- data/lib/rabarber/inputs/contexts/authorizational.rb +2 -7
- data/lib/rabarber/inputs/dynamic_rule.rb +3 -1
- data/lib/rabarber/inputs/non_empty_string.rb +2 -0
- data/lib/rabarber/inputs/non_empty_symbol.rb +2 -0
- data/lib/rabarber/inputs/role.rb +2 -0
- data/lib/rabarber/inputs/roles.rb +3 -1
- data/lib/rabarber/models/concerns/roleable.rb +50 -58
- data/lib/rabarber/models/role_management.rb +8 -2
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +7 -2
- data/rabarber.gemspec +2 -3
- metadata +13 -13
- data/lib/rabarber/models/role.rb +0 -124
- data/lib/rabarber/railtie.rb +0 -49
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/engine"
|
|
4
|
+
|
|
5
|
+
module Rabarber
|
|
6
|
+
class Engine < Rails::Engine
|
|
7
|
+
initializer "rabarber.include_roleable" do
|
|
8
|
+
Rails.autoloaders.each do |autoloader|
|
|
9
|
+
autoloader.on_load do |cpath, klass, _abspath|
|
|
10
|
+
klass.include Rabarber::Roleable if cpath == Rabarber::Configuration.user_model_name
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
initializer "rabarber.to_prepare" do |app|
|
|
16
|
+
app.config.to_prepare do
|
|
17
|
+
Rabarber::Core::Permissions.reset! unless app.config.eager_load
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
initializer "rabarber.deprecator" do |app|
|
|
22
|
+
app.deprecators[:rabarber] = Rabarber.deprecator
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
initializer "rabarber.extend_migration_helpers" do
|
|
26
|
+
ActiveSupport.on_load :active_record do
|
|
27
|
+
ActiveRecord::Migration.include Rabarber::MigrationHelpers
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -3,27 +3,16 @@
|
|
|
3
3
|
module Rabarber
|
|
4
4
|
module MigrationHelpers
|
|
5
5
|
def migrate_authorization_context!(old_context, new_context)
|
|
6
|
-
|
|
6
|
+
old_context_type = Rabarber::Inputs::NonEmptyString.new(old_context).process
|
|
7
|
+
new_context_type = Rabarber::Inputs::NonEmptyString.new(new_context).process
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
raise Rabarber::InvalidArgumentError, "Cannot migrate context to #{new_context.inspect}: class does not exist" unless new_context.to_s.safe_constantize
|
|
10
|
-
|
|
11
|
-
roles.update_all(context_type: new_context.to_s)
|
|
9
|
+
Rabarber::Role.where(context_type: old_context_type).update_all(context_type: new_context_type)
|
|
12
10
|
end
|
|
13
11
|
|
|
14
12
|
def delete_authorization_context!(context)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
raise Rabarber::InvalidArgumentError, "No roles exist in context #{context.inspect}" unless roles.exists?
|
|
13
|
+
context_type = Rabarber::Inputs::NonEmptyString.new(context).process
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
ActiveRecord::Base.connection.execute(
|
|
21
|
-
ActiveRecord::Base.sanitize_sql(
|
|
22
|
-
["DELETE FROM rabarber_roles_roleables WHERE role_id IN (?)", roles.pluck(:id)]
|
|
23
|
-
)
|
|
24
|
-
)
|
|
25
|
-
roles.delete_all
|
|
26
|
-
end
|
|
15
|
+
Rabarber::Role.delete_with_assignments(Rabarber::Role.where(context_type:))
|
|
27
16
|
end
|
|
28
17
|
end
|
|
29
18
|
end
|
data/lib/rabarber/inputs/base.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Rabarber
|
|
4
4
|
module Inputs
|
|
5
5
|
class Base
|
|
6
|
-
def initialize(value, optional: false, error:
|
|
6
|
+
def initialize(value, optional: false, error: nil, message: nil)
|
|
7
7
|
@value = value
|
|
8
8
|
@optional = optional
|
|
9
9
|
@error = error
|
|
@@ -22,8 +22,11 @@ module Rabarber
|
|
|
22
22
|
raise NotImplementedError
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def default_error = Rabarber::InvalidArgumentError
|
|
26
|
+
def default_message = nil
|
|
27
|
+
|
|
25
28
|
def raise_error
|
|
26
|
-
raise @error, @message
|
|
29
|
+
raise @error || default_error, @message || default_message
|
|
27
30
|
end
|
|
28
31
|
end
|
|
29
32
|
end
|
|
@@ -5,7 +5,9 @@ module Rabarber
|
|
|
5
5
|
class Boolean < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def default_message = "Expected a boolean, got `#{@value.inspect}`"
|
|
9
|
+
|
|
10
|
+
def validate_and_normalize = @value.equal?(true) || @value.equal?(false) ? @value : raise_error
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -10,20 +10,19 @@ module Rabarber
|
|
|
10
10
|
when Class
|
|
11
11
|
{ context_type: context.to_s, context_id: nil }
|
|
12
12
|
when ActiveRecord::Base
|
|
13
|
-
|
|
14
|
-
{ context_type: context.class.to_s, context_id: context.public_send(context.class.primary_key) }
|
|
15
|
-
else
|
|
16
|
-
context
|
|
13
|
+
{ context_type: context.class.to_s, context_id: context.id }
|
|
17
14
|
end
|
|
18
15
|
end
|
|
19
16
|
|
|
20
17
|
private
|
|
21
18
|
|
|
19
|
+
def default_error = Rabarber::InvalidContextError
|
|
20
|
+
def default_message = "Expected an instance of ActiveRecord model, a Class, or nil, got `#{@value.inspect}`"
|
|
21
|
+
|
|
22
22
|
def validate_and_normalize
|
|
23
23
|
return @value if @value.nil?
|
|
24
24
|
return @value if @value.is_a?(Class)
|
|
25
|
-
return @value if @value.is_a?(ActiveRecord::Base)
|
|
26
|
-
return @value if @value in { context_type: String | nil, context_id: String | Integer | nil }
|
|
25
|
+
return @value if @value.is_a?(ActiveRecord::Base) && @value.persisted?
|
|
27
26
|
|
|
28
27
|
raise_error
|
|
29
28
|
end
|
|
@@ -4,15 +4,10 @@ module Rabarber
|
|
|
4
4
|
module Inputs
|
|
5
5
|
module Contexts
|
|
6
6
|
class Authorizational < Rabarber::Inputs::Context
|
|
7
|
-
def resolve
|
|
8
|
-
result = process
|
|
9
|
-
return result if result.is_a?(Symbol) || result.is_a?(Proc)
|
|
10
|
-
|
|
11
|
-
super
|
|
12
|
-
end
|
|
13
|
-
|
|
14
7
|
private
|
|
15
8
|
|
|
9
|
+
def default_message = "Expected a Class, an instance of ActiveRecord model, a symbol, a string, or a proc, got `#{@value.inspect}`"
|
|
10
|
+
|
|
16
11
|
def validate_and_normalize
|
|
17
12
|
return @value if @value.is_a?(Proc)
|
|
18
13
|
return @value.to_sym if (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.present?
|
|
@@ -5,10 +5,12 @@ module Rabarber
|
|
|
5
5
|
class DynamicRule < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
+
def default_message = "Expected a symbol, a string, or a proc, got `#{@value.inspect}`"
|
|
9
|
+
|
|
8
10
|
def validate_and_normalize
|
|
9
11
|
return @value if @value.is_a?(Proc)
|
|
10
12
|
|
|
11
|
-
Rabarber::Inputs::NonEmptySymbol.new(@value, error: @error, message: @message).process
|
|
13
|
+
Rabarber::Inputs::NonEmptySymbol.new(@value, error: @error || default_error, message: @message || default_message).process
|
|
12
14
|
end
|
|
13
15
|
end
|
|
14
16
|
end
|
|
@@ -5,6 +5,8 @@ module Rabarber
|
|
|
5
5
|
class NonEmptySymbol < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
+
def default_message = "Expected a symbol or a string, got `#{@value.inspect}`"
|
|
9
|
+
|
|
8
10
|
def validate_and_normalize
|
|
9
11
|
raise_error unless (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.present?
|
|
10
12
|
|
data/lib/rabarber/inputs/role.rb
CHANGED
|
@@ -5,6 +5,8 @@ module Rabarber
|
|
|
5
5
|
class Role < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
+
def default_message = "Expected a symbol or a string containing only lowercase letters, numbers, and underscores, got `#{@value.inspect}`"
|
|
9
|
+
|
|
8
10
|
def validate_and_normalize
|
|
9
11
|
raise_error unless (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.to_s.match?(/\A[a-z0-9_]+\z/)
|
|
10
12
|
|
|
@@ -5,9 +5,11 @@ module Rabarber
|
|
|
5
5
|
class Roles < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
+
def default_message = "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got `#{@value.inspect}`"
|
|
9
|
+
|
|
8
10
|
def validate_and_normalize
|
|
9
11
|
Array(@value).map do |role|
|
|
10
|
-
Rabarber::Inputs::Role.new(role, error: @error, message: @message).process
|
|
12
|
+
Rabarber::Inputs::Role.new(role, error: @error || default_error, message: @message || default_message).process
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
end
|
|
@@ -9,113 +9,105 @@ module Rabarber
|
|
|
9
9
|
|
|
10
10
|
@@included = name
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
join_table: "rabarber_roles_roleables"
|
|
12
|
+
has_many :rabarber_role_assignments, class_name: "Rabarber::RoleAssignment", foreign_key: "roleable_id", inverse_of: :roleable, dependent: :delete_all
|
|
13
|
+
has_many :rabarber_roles, through: :rabarber_role_assignments, source: :role
|
|
15
14
|
|
|
16
15
|
scope :with_role, -> (*role_names, context: nil) {
|
|
17
16
|
joins(:rabarber_roles).where(
|
|
18
|
-
rabarber_roles: { name:
|
|
17
|
+
rabarber_roles: { name: Rabarber::Inputs::Roles.new(role_names).process, **Rabarber::Inputs::Context.new(context).resolve }
|
|
19
18
|
).distinct
|
|
20
19
|
}
|
|
21
|
-
|
|
22
|
-
class << self
|
|
23
|
-
def process_role_names(role_names)
|
|
24
|
-
Rabarber::Inputs::Roles.new(
|
|
25
|
-
role_names,
|
|
26
|
-
message: "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got #{role_names.inspect}"
|
|
27
|
-
).process
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def process_context(context)
|
|
31
|
-
Rabarber::Inputs::Context.new(
|
|
32
|
-
context,
|
|
33
|
-
error: Rabarber::InvalidContextError,
|
|
34
|
-
message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
|
|
35
|
-
).resolve
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
delegate :process_role_names, :process_context, to: :class, private: true
|
|
39
20
|
end
|
|
40
21
|
|
|
41
22
|
def roles(context: nil)
|
|
42
|
-
|
|
43
|
-
Rabarber::Core::Cache.fetch(roleable_id, processed_context) { rabarber_roles.list(context: processed_context) }
|
|
23
|
+
cached_roles(Rabarber::Inputs::Context.new(context).resolve)
|
|
44
24
|
end
|
|
45
25
|
|
|
46
26
|
def all_roles
|
|
47
|
-
Rabarber::Core::Cache.fetch(
|
|
27
|
+
Rabarber::Core::Cache.fetch(id, :all) { assigned_roles.list_all }
|
|
48
28
|
end
|
|
49
29
|
|
|
50
30
|
def has_role?(*role_names, context: nil)
|
|
51
|
-
processed_context =
|
|
52
|
-
processed_roles =
|
|
53
|
-
|
|
31
|
+
processed_context = Rabarber::Inputs::Context.new(context).resolve
|
|
32
|
+
processed_roles = Rabarber::Inputs::Roles.new(role_names).process
|
|
33
|
+
cached_roles(processed_context).intersect?(processed_roles)
|
|
54
34
|
end
|
|
55
35
|
|
|
56
36
|
def assign_roles(*role_names, context: nil, create_new: true)
|
|
57
|
-
|
|
58
|
-
|
|
37
|
+
processed_create_new = Rabarber::Inputs::Boolean.new(create_new).process
|
|
38
|
+
warn_if_unsaved_roleable(:assign_roles)
|
|
59
39
|
|
|
60
|
-
|
|
40
|
+
processed_role_names = Rabarber::Inputs::Roles.new(role_names).process
|
|
41
|
+
processed_context = Rabarber::Inputs::Context.new(context).resolve
|
|
61
42
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
43
|
+
Rabarber::Role.register_missing(processed_role_names, processed_context) if processed_create_new
|
|
44
|
+
|
|
45
|
+
role_ids = Rabarber::Role.where(
|
|
46
|
+
name: (processed_role_names - assigned_roles.names_in(processed_context)), **processed_context
|
|
47
|
+
).ids
|
|
65
48
|
|
|
66
|
-
if
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
rescue ActiveRecord::RecordNotUnique
|
|
49
|
+
if role_ids.any?
|
|
50
|
+
if persisted?
|
|
51
|
+
rabarber_role_assignments.insert_all(role_ids.map { { role_id: _1 } })
|
|
70
52
|
rabarber_roles.reset
|
|
53
|
+
else
|
|
54
|
+
rabarber_roles << Rabarber::Role.where(id: role_ids)
|
|
71
55
|
end
|
|
72
56
|
delete_roleable_cache(contexts: [processed_context])
|
|
73
57
|
end
|
|
74
58
|
|
|
75
|
-
|
|
59
|
+
cached_roles(processed_context)
|
|
76
60
|
end
|
|
77
61
|
|
|
78
62
|
def revoke_roles(*role_names, context: nil)
|
|
79
|
-
|
|
80
|
-
processed_context = process_context(context)
|
|
63
|
+
warn_if_unsaved_roleable(:revoke_roles)
|
|
81
64
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
65
|
+
processed_role_names = Rabarber::Inputs::Roles.new(role_names).process
|
|
66
|
+
processed_context = Rabarber::Inputs::Context.new(context).resolve
|
|
85
67
|
|
|
86
|
-
if
|
|
87
|
-
|
|
68
|
+
if rabarber_role_assignments.where(role: Rabarber::Role.where(name: processed_role_names, **processed_context)).delete_all.positive?
|
|
69
|
+
rabarber_roles.reset
|
|
88
70
|
delete_roleable_cache(contexts: [processed_context])
|
|
89
71
|
end
|
|
90
72
|
|
|
91
|
-
|
|
73
|
+
cached_roles(processed_context)
|
|
92
74
|
end
|
|
93
75
|
|
|
94
76
|
def revoke_all_roles
|
|
95
|
-
|
|
77
|
+
warn_if_unsaved_roleable(:revoke_all_roles)
|
|
96
78
|
|
|
97
|
-
|
|
79
|
+
roles = assigned_roles.pluck(:id, :context_type, :context_id)
|
|
98
80
|
|
|
99
|
-
|
|
81
|
+
rabarber_role_assignments.where(role_id: roles.map(&:first)).delete_all
|
|
82
|
+
rabarber_role_assignments.reset
|
|
83
|
+
rabarber_roles.reset
|
|
100
84
|
|
|
101
|
-
delete_roleable_cache(contexts:)
|
|
85
|
+
delete_roleable_cache(contexts: roles.map { |_, context_type, context_id| { context_type:, context_id: } }.uniq) if roles.any?
|
|
102
86
|
|
|
103
87
|
[]
|
|
104
88
|
end
|
|
105
89
|
|
|
106
90
|
private
|
|
107
91
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
92
|
+
def warn_if_unsaved_roleable(method_name)
|
|
93
|
+
return unless new_record?
|
|
94
|
+
|
|
95
|
+
Rabarber.deprecator.warn(
|
|
96
|
+
"Calling ##{method_name} on an unsaved record is deprecated. Save the record before managing roles.",
|
|
97
|
+
caller_locations(2)
|
|
98
|
+
)
|
|
111
99
|
end
|
|
112
100
|
|
|
113
|
-
def
|
|
114
|
-
Rabarber::
|
|
101
|
+
def assigned_roles
|
|
102
|
+
Rabarber::Role.where(id: rabarber_role_assignments.select(:role_id))
|
|
115
103
|
end
|
|
116
104
|
|
|
117
|
-
def
|
|
118
|
-
|
|
105
|
+
def cached_roles(processed_context)
|
|
106
|
+
Rabarber::Core::Cache.fetch(id, processed_context) { assigned_roles.names_in(processed_context) }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def delete_roleable_cache(contexts:)
|
|
110
|
+
Rabarber::Core::Cache.delete(*contexts.map { [id, _1] }, [id, :all])
|
|
119
111
|
end
|
|
120
112
|
end
|
|
121
113
|
end
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/core_ext/module/deprecation"
|
|
4
|
+
|
|
3
5
|
module Rabarber
|
|
4
6
|
module RoleManagement
|
|
5
7
|
def roles(context: nil) = Rabarber::Role.list(context:)
|
|
6
8
|
def all_roles = Rabarber::Role.list_all
|
|
7
|
-
def create_role(name, context: nil) = Rabarber::Role.
|
|
9
|
+
def create_role(name, context: nil) = Rabarber::Role.deprecated_register(name, context:)
|
|
10
|
+
def create_roles(*names, context: nil) = Rabarber::Role.register(names, context:)
|
|
8
11
|
def rename_role(old_name, new_name, context: nil, force: false) = Rabarber::Role.amend(old_name, new_name, context:, force:)
|
|
9
|
-
def delete_role(name, context: nil, force: false) = Rabarber::Role.
|
|
12
|
+
def delete_role(name, context: nil, force: false) = Rabarber::Role.deprecated_drop(name, context:, force:)
|
|
13
|
+
def delete_roles(*names, context: nil, force: false) = Rabarber::Role.drop(names, context:, force:)
|
|
10
14
|
def prune = Rabarber::Role.prune # rubocop:disable Rails/Delegate
|
|
15
|
+
|
|
16
|
+
deprecate create_role: :create_roles, delete_role: :delete_roles, deprecator: Rabarber.deprecator
|
|
11
17
|
end
|
|
12
18
|
end
|
data/lib/rabarber/version.rb
CHANGED
data/lib/rabarber.rb
CHANGED
|
@@ -25,6 +25,12 @@ module Rabarber
|
|
|
25
25
|
class InvalidContextError < Rabarber::InvalidArgumentError; end
|
|
26
26
|
class NotFoundError < Rabarber::Error; end
|
|
27
27
|
|
|
28
|
+
DEPRECATOR = ActiveSupport::Deprecation.new("7.0", "Rabarber")
|
|
29
|
+
|
|
30
|
+
def self.deprecator
|
|
31
|
+
DEPRECATOR
|
|
32
|
+
end
|
|
33
|
+
|
|
28
34
|
delegate :configure, to: Rabarber::Configuration
|
|
29
35
|
module_function :configure
|
|
30
36
|
end
|
|
@@ -37,11 +43,10 @@ require_relative "rabarber/controllers/concerns/authorization"
|
|
|
37
43
|
require_relative "rabarber/helpers/helpers"
|
|
38
44
|
require_relative "rabarber/helpers/migration_helpers"
|
|
39
45
|
require_relative "rabarber/models/concerns/roleable"
|
|
40
|
-
require_relative "rabarber/models/role"
|
|
41
46
|
require_relative "rabarber/models/role_management"
|
|
42
47
|
|
|
43
48
|
Rabarber.extend Rabarber::RoleManagement
|
|
44
49
|
|
|
45
50
|
require_relative "rabarber/core/permissions"
|
|
46
51
|
|
|
47
|
-
require_relative "rabarber/
|
|
52
|
+
require_relative "rabarber/engine"
|
data/rabarber.gemspec
CHANGED
|
@@ -7,14 +7,13 @@ Gem::Specification.new do |spec|
|
|
|
7
7
|
spec.version = Rabarber::VERSION
|
|
8
8
|
spec.authors = ["enjaku4", "trafium"]
|
|
9
9
|
spec.email = ["contact@brownbox.dev"]
|
|
10
|
-
spec.homepage = "https://github.com/
|
|
10
|
+
spec.homepage = "https://github.com/brownboxdev/rabarber-authorization"
|
|
11
11
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
12
12
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
13
13
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
14
14
|
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
15
15
|
spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/README.md"
|
|
16
16
|
spec.metadata["mailing_list_uri"] = "#{spec.homepage}/discussions"
|
|
17
|
-
spec.metadata["funding_uri"] = "https://github.com/sponsors/enjaku4"
|
|
18
17
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
19
18
|
spec.summary = "Simple role-based authorization library for Ruby on Rails"
|
|
20
19
|
spec.description = "Simple role-based authorization for Rails applications with multi-tenancy support"
|
|
@@ -23,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
|
23
22
|
|
|
24
23
|
spec.files = [
|
|
25
24
|
"rabarber.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"
|
|
26
|
-
] + Dir.glob("lib/**/*")
|
|
25
|
+
] + Dir.glob("{app,lib}/**/*")
|
|
27
26
|
|
|
28
27
|
spec.require_paths = ["lib"]
|
|
29
28
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rabarber
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.0
|
|
4
|
+
version: 6.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- enjaku4
|
|
8
8
|
- trafium
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -41,6 +41,8 @@ files:
|
|
|
41
41
|
- CHANGELOG.md
|
|
42
42
|
- LICENSE.txt
|
|
43
43
|
- README.md
|
|
44
|
+
- app/models/rabarber/role.rb
|
|
45
|
+
- app/models/rabarber/role_assignment.rb
|
|
44
46
|
- lib/generators/rabarber/roles_generator.rb
|
|
45
47
|
- lib/generators/rabarber/templates/create_rabarber_roles.rb.erb
|
|
46
48
|
- lib/rabarber.rb
|
|
@@ -51,6 +53,7 @@ files:
|
|
|
51
53
|
- lib/rabarber/core/permissions.rb
|
|
52
54
|
- lib/rabarber/core/roleable.rb
|
|
53
55
|
- lib/rabarber/core/rule.rb
|
|
56
|
+
- lib/rabarber/engine.rb
|
|
54
57
|
- lib/rabarber/helpers/helpers.rb
|
|
55
58
|
- lib/rabarber/helpers/migration_helpers.rb
|
|
56
59
|
- lib/rabarber/inputs/base.rb
|
|
@@ -64,22 +67,19 @@ files:
|
|
|
64
67
|
- lib/rabarber/inputs/role.rb
|
|
65
68
|
- lib/rabarber/inputs/roles.rb
|
|
66
69
|
- lib/rabarber/models/concerns/roleable.rb
|
|
67
|
-
- lib/rabarber/models/role.rb
|
|
68
70
|
- lib/rabarber/models/role_management.rb
|
|
69
|
-
- lib/rabarber/railtie.rb
|
|
70
71
|
- lib/rabarber/version.rb
|
|
71
72
|
- rabarber.gemspec
|
|
72
|
-
homepage: https://github.com/
|
|
73
|
+
homepage: https://github.com/brownboxdev/rabarber-authorization
|
|
73
74
|
licenses:
|
|
74
75
|
- MIT
|
|
75
76
|
metadata:
|
|
76
|
-
homepage_uri: https://github.com/
|
|
77
|
-
source_code_uri: https://github.com/
|
|
78
|
-
changelog_uri: https://github.com/
|
|
79
|
-
bug_tracker_uri: https://github.com/
|
|
80
|
-
documentation_uri: https://github.com/
|
|
81
|
-
mailing_list_uri: https://github.com/
|
|
82
|
-
funding_uri: https://github.com/sponsors/enjaku4
|
|
77
|
+
homepage_uri: https://github.com/brownboxdev/rabarber-authorization
|
|
78
|
+
source_code_uri: https://github.com/brownboxdev/rabarber-authorization
|
|
79
|
+
changelog_uri: https://github.com/brownboxdev/rabarber-authorization/blob/main/CHANGELOG.md
|
|
80
|
+
bug_tracker_uri: https://github.com/brownboxdev/rabarber-authorization/issues
|
|
81
|
+
documentation_uri: https://github.com/brownboxdev/rabarber-authorization/blob/main/README.md
|
|
82
|
+
mailing_list_uri: https://github.com/brownboxdev/rabarber-authorization/discussions
|
|
83
83
|
rubygems_mfa_required: 'true'
|
|
84
84
|
rdoc_options: []
|
|
85
85
|
require_paths:
|
|
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
98
98
|
- !ruby/object:Gem::Version
|
|
99
99
|
version: '0'
|
|
100
100
|
requirements: []
|
|
101
|
-
rubygems_version: 4.0.
|
|
101
|
+
rubygems_version: 4.0.16
|
|
102
102
|
specification_version: 4
|
|
103
103
|
summary: Simple role-based authorization library for Ruby on Rails
|
|
104
104
|
test_files: []
|
data/lib/rabarber/models/role.rb
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rabarber
|
|
4
|
-
class Role < ActiveRecord::Base
|
|
5
|
-
self.table_name = "rabarber_roles"
|
|
6
|
-
|
|
7
|
-
belongs_to :context, polymorphic: true, optional: true
|
|
8
|
-
|
|
9
|
-
class << self
|
|
10
|
-
def list(context: nil)
|
|
11
|
-
where(process_context(context)).pluck(:name).map(&:to_sym)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def list_all
|
|
15
|
-
includes(:context).each_with_object({}) do |role, hash|
|
|
16
|
-
(hash[role.context] ||= []) << role.name.to_sym
|
|
17
|
-
rescue ActiveRecord::RecordNotFound
|
|
18
|
-
next
|
|
19
|
-
end
|
|
20
|
-
rescue NameError => e
|
|
21
|
-
raise Rabarber::NotFoundError, "Context not found: class #{e.name} may have been renamed or deleted"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def register(name, context: nil)
|
|
25
|
-
name = process_role_name(name)
|
|
26
|
-
processed_context = process_context(context)
|
|
27
|
-
|
|
28
|
-
return false if exists?(name:, **processed_context)
|
|
29
|
-
|
|
30
|
-
!!create!(name:, **processed_context)
|
|
31
|
-
rescue ActiveRecord::RecordNotUnique
|
|
32
|
-
false
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def amend(old_name, new_name, context: nil, force: false)
|
|
36
|
-
processed_context = process_context(context)
|
|
37
|
-
role = find_by(name: process_role_name(old_name), **processed_context)
|
|
38
|
-
|
|
39
|
-
raise Rabarber::NotFoundError, "Role not found" unless role
|
|
40
|
-
|
|
41
|
-
name = process_role_name(new_name)
|
|
42
|
-
|
|
43
|
-
return false if exists?(name:, **processed_context) || role.roleables.exists? && !force
|
|
44
|
-
|
|
45
|
-
role.update!(name:)
|
|
46
|
-
|
|
47
|
-
delete_roleables_cache(role.roleables.pluck(:id), context: processed_context)
|
|
48
|
-
|
|
49
|
-
true
|
|
50
|
-
rescue ActiveRecord::RecordNotUnique
|
|
51
|
-
false
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def drop(name, context: nil, force: false)
|
|
55
|
-
processed_context = process_context(context)
|
|
56
|
-
role = find_by(name: process_role_name(name), **processed_context)
|
|
57
|
-
|
|
58
|
-
raise Rabarber::NotFoundError, "Role not found" unless role
|
|
59
|
-
|
|
60
|
-
return false if role.roleables.exists? && !force
|
|
61
|
-
|
|
62
|
-
roleable_ids = role.roleables.pluck(:id)
|
|
63
|
-
|
|
64
|
-
role.destroy!
|
|
65
|
-
|
|
66
|
-
delete_roleables_cache(roleable_ids, context: processed_context)
|
|
67
|
-
|
|
68
|
-
true
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def prune
|
|
72
|
-
orphaned_roles = where.not(context_id: nil).includes(:context).filter_map do |role|
|
|
73
|
-
!role.context
|
|
74
|
-
rescue ActiveRecord::RecordNotFound
|
|
75
|
-
role.id
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
return if orphaned_roles.empty?
|
|
79
|
-
|
|
80
|
-
ActiveRecord::Base.transaction do
|
|
81
|
-
ActiveRecord::Base.connection.execute(
|
|
82
|
-
ActiveRecord::Base.sanitize_sql(
|
|
83
|
-
["DELETE FROM rabarber_roles_roleables WHERE role_id IN (?)", orphaned_roles]
|
|
84
|
-
)
|
|
85
|
-
)
|
|
86
|
-
where(id: orphaned_roles).delete_all
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
Rabarber::Core::Cache.clear
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
private
|
|
93
|
-
|
|
94
|
-
def delete_roleables_cache(roleable_ids, context:)
|
|
95
|
-
Rabarber::Core::Cache.delete(*roleable_ids.flat_map { [[_1, context], [_1, :all]] })
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def process_role_name(name)
|
|
99
|
-
Rabarber::Inputs::Role.new(
|
|
100
|
-
name,
|
|
101
|
-
message: "Expected a symbol or a string containing only lowercase letters, numbers, and underscores, got #{name.inspect}"
|
|
102
|
-
).process
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def process_context(context)
|
|
106
|
-
Rabarber::Inputs::Context.new(
|
|
107
|
-
context,
|
|
108
|
-
error: Rabarber::InvalidContextError,
|
|
109
|
-
message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
|
|
110
|
-
).resolve
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def context
|
|
115
|
-
return context_type.constantize if context_type.present? && context_id.blank?
|
|
116
|
-
|
|
117
|
-
record = super
|
|
118
|
-
|
|
119
|
-
raise ActiveRecord::RecordNotFound.new(nil, context_type, nil, context_id) if context_id.present? && !record
|
|
120
|
-
|
|
121
|
-
record
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
end
|