rabarber 5.1.0 → 5.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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +53 -30
- data/lib/rabarber/configuration.rb +43 -25
- data/lib/rabarber/controllers/concerns/authorization.rb +28 -5
- data/lib/rabarber/core/cache.rb +1 -1
- data/lib/rabarber/core/roleable.rb +3 -3
- data/lib/rabarber/core/rule.rb +6 -5
- data/lib/rabarber/inputs.rb +62 -0
- data/lib/rabarber/models/concerns/has_roles.rb +10 -2
- data/lib/rabarber/models/role.rb +12 -5
- data/lib/rabarber/railtie.rb +1 -1
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +13 -24
- data/rabarber.gemspec +2 -0
- metadata +30 -12
- data/lib/rabarber/input/action.rb +0 -21
- data/lib/rabarber/input/ar_model.rb +0 -23
- data/lib/rabarber/input/authorization_context.rb +0 -25
- data/lib/rabarber/input/base.rb +0 -37
- data/lib/rabarber/input/context.rb +0 -33
- data/lib/rabarber/input/dynamic_rule.rb +0 -21
- data/lib/rabarber/input/role.rb +0 -23
- data/lib/rabarber/input/roles.rb +0 -25
- data/lib/rabarber/input/types/boolean.rb +0 -23
- data/lib/rabarber/input/types/proc.rb +0 -23
- data/lib/rabarber/input/types/symbol.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: 841b259e40b7e446647ee02fcca64cd183c64978c194edb6917ba344d9f12e92
|
4
|
+
data.tar.gz: 044c1aa8248f7b1b16f1c78648a0b4cafb0451f31d8fed5b72f6d3343a645c48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0880247a0c7f1af21dbb8099880d24ec200295e54f976855aac577fba55b692fee41a7ddfaf10f83848dfba20e2c0ac8289eb7bc94df21cabceb04da47925c1
|
7
|
+
data.tar.gz: 024e3093a753d53f158532c829ca245b159448d19901d884b4c09518e70b81d39d710ee0867339ca064bc0519fd06ef53a7af507149d29e80a2a2216ad92a096
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -18,8 +18,8 @@ Rabarber is a role-based authorization library for Ruby on Rails that focuses on
|
|
18
18
|
**Gem Usage:**
|
19
19
|
- [Installation](#installation)
|
20
20
|
- [Configuration](#configuration)
|
21
|
-
- [Role Management](#role-management)
|
22
21
|
- [User Role Methods](#user-role-methods)
|
22
|
+
- [Role Management](#role-management)
|
23
23
|
- [Controller Authorization](#controller-authorization)
|
24
24
|
- [Dynamic Rules](#dynamic-rules)
|
25
25
|
- [Multi-tenancy / Context](#multi-tenancy--context)
|
@@ -27,7 +27,7 @@ Rabarber is a role-based authorization library for Ruby on Rails that focuses on
|
|
27
27
|
|
28
28
|
|
29
29
|
**Community Resources:**
|
30
|
-
- [Contributing](#contributing)
|
30
|
+
- [Getting Help and Contributing](#getting-help-and-contributing)
|
31
31
|
- [License](#license)
|
32
32
|
- [Code of Conduct](#code-of-conduct)
|
33
33
|
- [Old Versions](#old-versions)
|
@@ -80,30 +80,6 @@ To clear the role cache manually:
|
|
80
80
|
Rabarber::Cache.clear
|
81
81
|
```
|
82
82
|
|
83
|
-
## Role Management
|
84
|
-
|
85
|
-
### Direct Role Operations
|
86
|
-
|
87
|
-
```rb
|
88
|
-
# Create a new role
|
89
|
-
Rabarber::Role.add(:admin)
|
90
|
-
|
91
|
-
# Rename a role
|
92
|
-
Rabarber::Role.rename(:admin, :administrator)
|
93
|
-
Rabarber::Role.rename(:admin, :administrator, force: true) # Force if role is assigned to users
|
94
|
-
|
95
|
-
# Remove a role
|
96
|
-
Rabarber::Role.remove(:admin)
|
97
|
-
Rabarber::Role.remove(:admin, force: true) # Force if role is assigned to users
|
98
|
-
|
99
|
-
# List available roles
|
100
|
-
Rabarber::Role.names
|
101
|
-
Rabarber::Role.all_names # All roles grouped by context
|
102
|
-
|
103
|
-
# Get users assigned to a role
|
104
|
-
Rabarber::Role.assignees(:admin)
|
105
|
-
```
|
106
|
-
|
107
83
|
## User Role Methods
|
108
84
|
|
109
85
|
Your user model is automatically augmented with role management methods:
|
@@ -137,6 +113,30 @@ user.roles
|
|
137
113
|
user.all_roles
|
138
114
|
```
|
139
115
|
|
116
|
+
## Role Management
|
117
|
+
|
118
|
+
### Direct Role Operations
|
119
|
+
|
120
|
+
```rb
|
121
|
+
# Create a new role
|
122
|
+
Rabarber::Role.add(:admin)
|
123
|
+
|
124
|
+
# Rename a role
|
125
|
+
Rabarber::Role.rename(:admin, :administrator)
|
126
|
+
Rabarber::Role.rename(:admin, :administrator, force: true) # Force if role is assigned to users
|
127
|
+
|
128
|
+
# Remove a role
|
129
|
+
Rabarber::Role.remove(:admin)
|
130
|
+
Rabarber::Role.remove(:admin, force: true) # Force if role is assigned to users
|
131
|
+
|
132
|
+
# List available roles
|
133
|
+
Rabarber::Role.names
|
134
|
+
Rabarber::Role.all_names # All roles grouped by context
|
135
|
+
|
136
|
+
# Get users assigned to a role
|
137
|
+
Rabarber::Role.assignees(:admin)
|
138
|
+
```
|
139
|
+
|
140
140
|
## Controller Authorization
|
141
141
|
|
142
142
|
### Basic Setup
|
@@ -182,9 +182,9 @@ class TicketsController < ApplicationController
|
|
182
182
|
# Accessible to admin, manager, and support roles
|
183
183
|
end
|
184
184
|
|
185
|
-
grant_access action: :destroy, roles: :
|
185
|
+
grant_access action: :destroy, roles: :owner, context: -> { Ticket.find(params[:id]) }
|
186
186
|
def destroy
|
187
|
-
# Accessible to admin
|
187
|
+
# Accessible to admin and owner of the ticket
|
188
188
|
end
|
189
189
|
end
|
190
190
|
```
|
@@ -203,7 +203,9 @@ class InvoicesController < BaseController
|
|
203
203
|
|
204
204
|
grant_access action: :index, roles: :manager
|
205
205
|
grant_access action: :index, roles: :supervisor
|
206
|
-
|
206
|
+
def index
|
207
|
+
# Index is accessible to admin, accountant, manager, and supervisor
|
208
|
+
end
|
207
209
|
end
|
208
210
|
```
|
209
211
|
|
@@ -218,7 +220,14 @@ end
|
|
218
220
|
|
219
221
|
class MixedController < ApplicationController
|
220
222
|
grant_access action: :index # Unrestricted index action
|
223
|
+
def index
|
224
|
+
# Accessible to all users
|
225
|
+
end
|
226
|
+
|
221
227
|
grant_access action: :show, roles: :member # Restricted show action
|
228
|
+
def show
|
229
|
+
# Accessible to members only
|
230
|
+
end
|
222
231
|
end
|
223
232
|
```
|
224
233
|
|
@@ -242,6 +251,8 @@ class ApplicationController < ActionController::Base
|
|
242
251
|
end
|
243
252
|
```
|
244
253
|
|
254
|
+
By default, Rabarber will redirect back (HTML format) or return 403 (other formats).
|
255
|
+
|
245
256
|
## Dynamic Rules
|
246
257
|
|
247
258
|
Add conditional logic to authorization rules:
|
@@ -253,9 +264,15 @@ class OrdersController < ApplicationController
|
|
253
264
|
|
254
265
|
# Proc-based conditions
|
255
266
|
grant_access action: :show, roles: :client, if: -> { current_user.company_id == Order.find(params[:id]).company_id }
|
267
|
+
def show
|
268
|
+
# Accessible to company managers unless suspended, and to clients if the client's company matches the order's company
|
269
|
+
end
|
256
270
|
|
257
271
|
# Dynamic-only rules (no roles required, can be used with custom policies)
|
258
272
|
grant_access action: :index, if: -> { OrdersPolicy.new(current_user).can_access?(:index) }
|
273
|
+
def index
|
274
|
+
# Accessible to company managers unless suspended, and to users based on custom policy logic
|
275
|
+
end
|
259
276
|
|
260
277
|
private
|
261
278
|
|
@@ -301,9 +318,15 @@ class ProjectsController < ApplicationController
|
|
301
318
|
|
302
319
|
# Instance-based context (method)
|
303
320
|
grant_access action: :show, roles: :member, context: :current_project
|
321
|
+
def show
|
322
|
+
# Accessible to Project admin and members of the current project
|
323
|
+
end
|
304
324
|
|
305
325
|
# Instance-based context (proc)
|
306
326
|
grant_access action: :update, roles: :owner, context: -> { Project.find(params[:id]) }
|
327
|
+
def update
|
328
|
+
# Accessible to Project admin and owner of the current project
|
329
|
+
end
|
307
330
|
|
308
331
|
private
|
309
332
|
|
@@ -356,7 +379,7 @@ Use conditional rendering based on roles:
|
|
356
379
|
<% end %>
|
357
380
|
```
|
358
381
|
|
359
|
-
## Contributing
|
382
|
+
## Getting Help and Contributing
|
360
383
|
|
361
384
|
### Getting Help
|
362
385
|
Have a question or need assistance? Open a discussion in our [discussions section](https://github.com/brownboxdev/rabarber/discussions) for:
|
@@ -1,36 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "dry-configurable"
|
4
4
|
|
5
5
|
module Rabarber
|
6
|
-
|
7
|
-
|
6
|
+
module Configuration
|
7
|
+
extend Dry::Configurable
|
8
8
|
|
9
|
-
|
10
|
-
attr_accessor :user_model_name
|
9
|
+
module_function
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
11
|
+
setting :cache_enabled,
|
12
|
+
default: true,
|
13
|
+
reader: true,
|
14
|
+
constructor: -> (value) do
|
15
|
+
Rabarber::Inputs.process(
|
16
|
+
value,
|
17
|
+
as: :boolean,
|
18
|
+
error: Rabarber::ConfigurationError,
|
19
|
+
message: "Invalid configuration `cache_enabled`, expected a boolean, got #{value.inspect}"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
setting :current_user_method,
|
23
|
+
default: :current_user,
|
24
|
+
reader: true,
|
25
|
+
constructor: -> (value) do
|
26
|
+
Rabarber::Inputs.process(
|
27
|
+
value,
|
28
|
+
as: :symbol,
|
29
|
+
error: Rabarber::ConfigurationError,
|
30
|
+
message: "Invalid configuration `current_user_method`, expected a symbol or a string, got #{value.inspect}"
|
31
|
+
)
|
32
|
+
end
|
33
|
+
setting :user_model_name,
|
34
|
+
default: "User",
|
35
|
+
reader: true,
|
36
|
+
constructor: -> (value) do
|
37
|
+
Rabarber::Inputs.process(
|
38
|
+
value,
|
39
|
+
as: :string,
|
40
|
+
error: Rabarber::ConfigurationError,
|
41
|
+
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{value.inspect}"
|
42
|
+
)
|
43
|
+
end
|
29
44
|
|
30
45
|
def user_model
|
31
|
-
Rabarber::
|
32
|
-
|
33
|
-
|
46
|
+
Rabarber::Inputs.process(
|
47
|
+
user_model_name,
|
48
|
+
as: :model,
|
49
|
+
error: Rabarber::ConfigurationError,
|
50
|
+
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{user_model_name.inspect}"
|
51
|
+
)
|
34
52
|
end
|
35
53
|
end
|
36
54
|
end
|
@@ -21,11 +21,34 @@ module Rabarber
|
|
21
21
|
def grant_access(action: nil, roles: nil, context: nil, if: nil, unless: nil)
|
22
22
|
Rabarber::Core::Permissions.add(
|
23
23
|
self,
|
24
|
-
Rabarber::
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
Rabarber::Inputs.process(
|
25
|
+
action,
|
26
|
+
as: :symbol,
|
27
|
+
optional: true,
|
28
|
+
message: "Expected a symbol or a string, got #{action.inspect}"
|
29
|
+
),
|
30
|
+
Rabarber::Inputs.process(
|
31
|
+
roles,
|
32
|
+
as: :roles,
|
33
|
+
message: "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got #{roles.inspect}"
|
34
|
+
),
|
35
|
+
Rabarber::Inputs.process(
|
36
|
+
context,
|
37
|
+
as: :authorization_context,
|
38
|
+
message: "Expected a Class, an instance of ActiveRecord model, a symbol, a string, or a proc, got #{context.inspect}"
|
39
|
+
),
|
40
|
+
Rabarber::Inputs.process(
|
41
|
+
binding.local_variable_get(:if),
|
42
|
+
as: :dynamic_rule,
|
43
|
+
optional: true,
|
44
|
+
message: "Expected a symbol, a string, or a proc, got #{binding.local_variable_get(:if).inspect}"
|
45
|
+
),
|
46
|
+
Rabarber::Inputs.process(
|
47
|
+
binding.local_variable_get(:unless),
|
48
|
+
as: :dynamic_rule,
|
49
|
+
optional: true,
|
50
|
+
message: "Expected a symbol, a string, or a proc, got #{binding.local_variable_get(:unless).inspect}"
|
51
|
+
)
|
29
52
|
)
|
30
53
|
end
|
31
54
|
end
|
data/lib/rabarber/core/cache.rb
CHANGED
@@ -4,12 +4,12 @@ module Rabarber
|
|
4
4
|
module Core
|
5
5
|
module Roleable
|
6
6
|
def roleable
|
7
|
-
current_roleable = send(Rabarber::Configuration.
|
7
|
+
current_roleable = send(Rabarber::Configuration.current_user_method)
|
8
8
|
|
9
|
-
unless current_roleable.is_a?(Rabarber::Configuration.
|
9
|
+
unless current_roleable.is_a?(Rabarber::Configuration.user_model)
|
10
10
|
raise(
|
11
11
|
Rabarber::Error,
|
12
|
-
"Expected `#{Rabarber::Configuration.
|
12
|
+
"Expected `#{Rabarber::Configuration.current_user_method}` to return an instance of #{Rabarber::Configuration.user_model_name}, got #{current_roleable.inspect}"
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|
data/lib/rabarber/core/rule.rb
CHANGED
@@ -35,11 +35,12 @@ module Rabarber
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def resolve_context(controller_instance)
|
38
|
-
case context
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
resolved_context = case context
|
39
|
+
when Proc then controller_instance.instance_exec(&context)
|
40
|
+
when Symbol then controller_instance.send(context)
|
41
|
+
else context
|
42
|
+
end
|
43
|
+
Rabarber::Inputs.process(resolved_context, as: :role_context, message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{resolved_context.inspect}")
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-types"
|
4
|
+
|
5
|
+
module Rabarber
|
6
|
+
module Inputs
|
7
|
+
extend self
|
8
|
+
|
9
|
+
include Dry.Types()
|
10
|
+
|
11
|
+
PROC_TYPE = self::Instance(Proc)
|
12
|
+
SYMBOL_TYPE = self::Coercible::Symbol.constrained(min_size: 1)
|
13
|
+
ROLE_TYPE = self::SYMBOL_TYPE.constrained(format: /\A[a-z0-9_]+\z/)
|
14
|
+
|
15
|
+
CONTEXT_TYPE = self::Strict::Class | self::Instance(ActiveRecord::Base) | self::Hash.schema(
|
16
|
+
context_type: self::Strict::String | self::Nil,
|
17
|
+
context_id: self::Strict::String | self::Strict::Integer | self::Nil
|
18
|
+
) | self::Nil
|
19
|
+
|
20
|
+
TYPES = {
|
21
|
+
boolean: self::Strict::Bool,
|
22
|
+
string: self::Strict::String.constrained(min_size: 1).constructor { _1.is_a?(::String) ? _1.strip : _1 },
|
23
|
+
symbol: self::SYMBOL_TYPE,
|
24
|
+
role: self::ROLE_TYPE,
|
25
|
+
roles: self::Array.of(self::ROLE_TYPE).constructor { Kernel::Array(_1) },
|
26
|
+
model: self::Strict::Class.constructor { _1.try(:safe_constantize) }.constrained(lt: ActiveRecord::Base),
|
27
|
+
dynamic_rule: self::SYMBOL_TYPE | self::PROC_TYPE,
|
28
|
+
role_context: self::CONTEXT_TYPE,
|
29
|
+
authorization_context: self::SYMBOL_TYPE | self::PROC_TYPE | self::CONTEXT_TYPE
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
def process(value, as:, optional: false, error: Rabarber::InvalidArgumentError, message: nil)
|
33
|
+
checker = type_for(as)
|
34
|
+
checker = checker.optional if optional
|
35
|
+
|
36
|
+
result = checker[value]
|
37
|
+
|
38
|
+
[:role_context, :authorization_context].include?(as) ? resolve_context(result) : result
|
39
|
+
rescue Dry::Types::CoercionError => e
|
40
|
+
raise error, message || e.message
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def type_for(name) = self::TYPES.fetch(name)
|
46
|
+
|
47
|
+
def resolve_context(value)
|
48
|
+
case value
|
49
|
+
when nil
|
50
|
+
{ context_type: nil, context_id: nil }
|
51
|
+
when Class
|
52
|
+
{ context_type: value.to_s, context_id: nil }
|
53
|
+
when ActiveRecord::Base
|
54
|
+
raise Dry::Types::CoercionError, "instance context not persisted" unless value.persisted?
|
55
|
+
|
56
|
+
{ context_type: value.class.to_s, context_id: value.public_send(value.class.primary_key) }
|
57
|
+
else
|
58
|
+
value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -81,11 +81,19 @@ module Rabarber
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def process_role_names(role_names)
|
84
|
-
Rabarber::
|
84
|
+
Rabarber::Inputs.process(
|
85
|
+
role_names,
|
86
|
+
as: :roles,
|
87
|
+
message: "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got #{role_names.inspect}"
|
88
|
+
)
|
85
89
|
end
|
86
90
|
|
87
91
|
def process_context(context)
|
88
|
-
Rabarber::
|
92
|
+
Rabarber::Inputs.process(
|
93
|
+
context,
|
94
|
+
as: :role_context,
|
95
|
+
message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
|
96
|
+
)
|
89
97
|
end
|
90
98
|
|
91
99
|
def delete_roleable_cache(contexts:)
|
data/lib/rabarber/models/role.rb
CHANGED
@@ -6,7 +6,7 @@ module Rabarber
|
|
6
6
|
|
7
7
|
belongs_to :context, polymorphic: true, optional: true
|
8
8
|
|
9
|
-
has_and_belongs_to_many :roleables, class_name: Rabarber::Configuration.
|
9
|
+
has_and_belongs_to_many :roleables, class_name: Rabarber::Configuration.user_model_name,
|
10
10
|
association_foreign_key: "roleable_id",
|
11
11
|
join_table: "rabarber_roles_roleables"
|
12
12
|
|
@@ -63,8 +63,7 @@ module Rabarber
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def assignees(name, context: nil)
|
66
|
-
find_by(name: process_role_name(name), **process_context(context))&.roleables ||
|
67
|
-
Rabarber::Configuration.instance.user_model.none
|
66
|
+
find_by(name: process_role_name(name), **process_context(context))&.roleables || Rabarber::Configuration.user_model.none
|
68
67
|
end
|
69
68
|
|
70
69
|
private
|
@@ -76,11 +75,19 @@ module Rabarber
|
|
76
75
|
end
|
77
76
|
|
78
77
|
def process_role_name(name)
|
79
|
-
Rabarber::
|
78
|
+
Rabarber::Inputs.process(
|
79
|
+
name,
|
80
|
+
as: :role,
|
81
|
+
message: "Expected a symbol or a string containing only lowercase letters, numbers, and underscores, got #{name.inspect}"
|
82
|
+
)
|
80
83
|
end
|
81
84
|
|
82
85
|
def process_context(context)
|
83
|
-
Rabarber::
|
86
|
+
Rabarber::Inputs.process(
|
87
|
+
context,
|
88
|
+
as: :role_context,
|
89
|
+
message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
|
90
|
+
)
|
84
91
|
end
|
85
92
|
end
|
86
93
|
|
data/lib/rabarber/railtie.rb
CHANGED
data/lib/rabarber/version.rb
CHANGED
data/lib/rabarber.rb
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "rabarber/version"
|
4
|
-
require_relative "rabarber/configuration"
|
5
4
|
|
6
5
|
require "active_record"
|
7
6
|
require "active_support"
|
8
7
|
|
9
|
-
require_relative "rabarber/
|
10
|
-
|
11
|
-
require_relative "rabarber/
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
8
|
+
require_relative "rabarber/inputs"
|
9
|
+
|
10
|
+
require_relative "rabarber/configuration"
|
11
|
+
|
12
|
+
module Rabarber
|
13
|
+
class Error < StandardError; end
|
14
|
+
class ConfigurationError < Rabarber::Error; end
|
15
|
+
class InvalidArgumentError < Rabarber::Error; end
|
16
|
+
class NotFoundError < Rabarber::Error; end
|
17
|
+
|
18
|
+
delegate :configure, to: Rabarber::Configuration
|
19
|
+
module_function :configure
|
20
|
+
end
|
20
21
|
|
21
22
|
require_relative "rabarber/core/cache"
|
22
23
|
|
@@ -32,15 +33,3 @@ require_relative "rabarber/core/permissions"
|
|
32
33
|
require_relative "rabarber/core/integrity_checker"
|
33
34
|
|
34
35
|
require_relative "rabarber/railtie"
|
35
|
-
|
36
|
-
module Rabarber
|
37
|
-
class Error < StandardError; end
|
38
|
-
class ConfigurationError < Rabarber::Error; end
|
39
|
-
class InvalidArgumentError < Rabarber::Error; end
|
40
|
-
class NotFoundError < Rabarber::Error; end
|
41
|
-
|
42
|
-
def configure
|
43
|
-
yield(Rabarber::Configuration.instance)
|
44
|
-
end
|
45
|
-
module_function :configure
|
46
|
-
end
|
data/rabarber.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabarber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.1.
|
4
|
+
version: 5.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- enjaku4
|
@@ -10,6 +10,34 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 1980-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-configurable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-types
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.8'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rails
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -50,17 +78,7 @@ files:
|
|
50
78
|
- lib/rabarber/core/rule.rb
|
51
79
|
- lib/rabarber/helpers/helpers.rb
|
52
80
|
- lib/rabarber/helpers/migration_helpers.rb
|
53
|
-
- lib/rabarber/
|
54
|
-
- lib/rabarber/input/ar_model.rb
|
55
|
-
- lib/rabarber/input/authorization_context.rb
|
56
|
-
- lib/rabarber/input/base.rb
|
57
|
-
- lib/rabarber/input/context.rb
|
58
|
-
- lib/rabarber/input/dynamic_rule.rb
|
59
|
-
- lib/rabarber/input/role.rb
|
60
|
-
- lib/rabarber/input/roles.rb
|
61
|
-
- lib/rabarber/input/types/boolean.rb
|
62
|
-
- lib/rabarber/input/types/proc.rb
|
63
|
-
- lib/rabarber/input/types/symbol.rb
|
81
|
+
- lib/rabarber/inputs.rb
|
64
82
|
- lib/rabarber/models/concerns/has_roles.rb
|
65
83
|
- lib/rabarber/models/role.rb
|
66
84
|
- lib/rabarber/railtie.rb
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class Action < Rabarber::Input::Base
|
6
|
-
def valid?
|
7
|
-
Rabarber::Input::Types::Symbol.new(value).valid? || value.nil?
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def processed_value
|
13
|
-
value&.to_sym
|
14
|
-
end
|
15
|
-
|
16
|
-
def default_error_message
|
17
|
-
"Action name must be a Symbol or a String"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class ArModel < Rabarber::Input::Base
|
6
|
-
def valid?
|
7
|
-
processed_value < ActiveRecord::Base
|
8
|
-
rescue NameError
|
9
|
-
false
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def processed_value
|
15
|
-
value.constantize
|
16
|
-
end
|
17
|
-
|
18
|
-
def default_error_message
|
19
|
-
"Value must be an ActiveRecord model"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class AuthorizationContext < Rabarber::Input::Base
|
6
|
-
def valid?
|
7
|
-
Rabarber::Input::Context.new(value).valid? || Rabarber::Input::DynamicRule.new(value).valid?
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def processed_value
|
13
|
-
case value
|
14
|
-
when String then value.to_sym
|
15
|
-
when Symbol, Proc then value
|
16
|
-
else Rabarber::Input::Context.new(value).process
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def default_error_message
|
21
|
-
"Context must be a Class, an instance of ActiveRecord model, a Symbol, a String, or a Proc"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/rabarber/input/base.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class Base
|
6
|
-
attr_reader :value, :error_type, :error_message
|
7
|
-
|
8
|
-
def initialize(value, error_type = Rabarber::InvalidArgumentError, error_message = nil)
|
9
|
-
@value = value
|
10
|
-
@error_type = error_type
|
11
|
-
@error_message = error_message || default_error_message
|
12
|
-
end
|
13
|
-
|
14
|
-
def process
|
15
|
-
valid? ? processed_value : raise_error
|
16
|
-
end
|
17
|
-
|
18
|
-
def valid?
|
19
|
-
raise NotImplementedError
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def processed_value
|
25
|
-
raise NotImplementedError
|
26
|
-
end
|
27
|
-
|
28
|
-
def default_error_message
|
29
|
-
raise NotImplementedError
|
30
|
-
end
|
31
|
-
|
32
|
-
def raise_error
|
33
|
-
raise error_type, error_message
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class Context < Rabarber::Input::Base
|
6
|
-
def valid?
|
7
|
-
value.nil? || value.is_a?(Class) || value.is_a?(ActiveRecord::Base) && value.persisted? || already_processed?
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def processed_value
|
13
|
-
case value
|
14
|
-
when nil then { context_type: nil, context_id: nil }
|
15
|
-
when Class then { context_type: value.to_s, context_id: nil }
|
16
|
-
when ActiveRecord::Base then { context_type: value.class.to_s, context_id: value.public_send(value.class.primary_key) }
|
17
|
-
else value
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def default_error_message
|
22
|
-
"Context must be a Class or an instance of ActiveRecord model"
|
23
|
-
end
|
24
|
-
|
25
|
-
def already_processed?
|
26
|
-
case value
|
27
|
-
in { context_type: NilClass | String, context_id: NilClass | String | Integer } then true
|
28
|
-
else false
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class DynamicRule < Rabarber::Input::Base
|
6
|
-
def valid?
|
7
|
-
Rabarber::Input::Types::Symbol.new(value).valid? || Rabarber::Input::Types::Proc.new(value).valid? || value.nil?
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
def processed_value
|
13
|
-
value.is_a?(String) ? value.to_sym : value
|
14
|
-
end
|
15
|
-
|
16
|
-
def default_error_message
|
17
|
-
"Dynamic rule must be a Symbol, a String, or a Proc"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/rabarber/input/role.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class Role < Rabarber::Input::Base
|
6
|
-
REGEX = /\A[a-z0-9_]+\z/
|
7
|
-
|
8
|
-
def valid?
|
9
|
-
Rabarber::Input::Types::Symbol.new(value).valid? && value.to_s.match?(REGEX)
|
10
|
-
end
|
11
|
-
|
12
|
-
private
|
13
|
-
|
14
|
-
def processed_value
|
15
|
-
value.to_sym
|
16
|
-
end
|
17
|
-
|
18
|
-
def default_error_message
|
19
|
-
"Role name must be a Symbol or a String and may only contain lowercase letters, numbers, and underscores"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/rabarber/input/roles.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
class Roles < Rabarber::Input::Base
|
6
|
-
def value
|
7
|
-
Array(super)
|
8
|
-
end
|
9
|
-
|
10
|
-
def valid?
|
11
|
-
value.all? { |role_name| Rabarber::Input::Role.new(role_name).valid? }
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def processed_value
|
17
|
-
value.map(&:to_sym)
|
18
|
-
end
|
19
|
-
|
20
|
-
def default_error_message
|
21
|
-
"Role names must be Symbols or Strings and may only contain lowercase letters, numbers, and underscores"
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
module Types
|
6
|
-
class Boolean < Rabarber::Input::Base
|
7
|
-
def valid?
|
8
|
-
[true, false].include?(value)
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def processed_value
|
14
|
-
value
|
15
|
-
end
|
16
|
-
|
17
|
-
def default_error_message
|
18
|
-
"Value must be a Boolean"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
module Types
|
6
|
-
class Proc < Rabarber::Input::Base
|
7
|
-
def valid?
|
8
|
-
value.is_a?(::Proc)
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def processed_value
|
14
|
-
value
|
15
|
-
end
|
16
|
-
|
17
|
-
def default_error_message
|
18
|
-
"Value must be a Proc"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rabarber
|
4
|
-
module Input
|
5
|
-
module Types
|
6
|
-
class Symbol < Rabarber::Input::Base
|
7
|
-
def valid?
|
8
|
-
(value.is_a?(::Symbol) || value.is_a?(String)) && value.present?
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def processed_value
|
14
|
-
value.to_sym
|
15
|
-
end
|
16
|
-
|
17
|
-
def default_error_message
|
18
|
-
"Value must be a Symbol or a String"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|