rabarber 5.2.5 → 6.0.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 +41 -0
- data/README.md +71 -50
- data/lib/generators/rabarber/templates/create_rabarber_roles.rb.erb +5 -0
- data/lib/rabarber/configuration.rb +50 -36
- data/lib/rabarber/controllers/concerns/authorization.rb +1 -1
- data/lib/rabarber/core/cache.rb +29 -13
- data/lib/rabarber/inputs/base.rb +4 -9
- data/lib/rabarber/inputs/boolean.rb +1 -1
- data/lib/rabarber/inputs/context.rb +7 -8
- data/lib/rabarber/inputs/contexts/authorizational.rb +13 -1
- data/lib/rabarber/inputs/dynamic_rule.rb +5 -1
- data/lib/rabarber/inputs/model.rb +6 -1
- data/lib/rabarber/inputs/non_empty_string.rb +3 -1
- data/lib/rabarber/inputs/non_empty_symbol.rb +15 -0
- data/lib/rabarber/inputs/role.rb +5 -1
- data/lib/rabarber/inputs/roles.rb +4 -2
- data/lib/rabarber/models/concerns/roleable.rb +12 -8
- data/lib/rabarber/models/role.rb +21 -37
- data/lib/rabarber/models/role_management.rb +12 -0
- data/lib/rabarber/railtie.rb +5 -0
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +4 -10
- data/rabarber.gemspec +2 -4
- metadata +8 -35
- data/lib/rabarber/inputs/symbol.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6cb92ea0f4a25ee7dd730aae35b07d16230434393882cd0833d853117e7887e9
|
|
4
|
+
data.tar.gz: 3056af62a93df97b88f708dc725e0b54c92cfc198aad225ba80262fcfd36a31a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1e599ea0709127eeed62dd0aae019f9dee5a73843e08671089e1a84b16e13a2db4ad8f908080b51f3a5a8d2fa82d9a7f21efed638dd6e1054f5c80fdb8b5f423
|
|
7
|
+
data.tar.gz: 0b2548c277de1beb990d9ce0321b0989d7358c521e3cb0d20994cf389ea931feb288dc670d7e886c6a1ccff29dc4d7bdd7c1a07885f0ad327f7b94e3cf8d6f82
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
## v6.0.1
|
|
2
|
+
|
|
3
|
+
### Bugs:
|
|
4
|
+
|
|
5
|
+
- Fixed role management methods raising `NameError` when the configured user model is not named `User`
|
|
6
|
+
- Fixed a race condition where outdated roles could remain cached after being changed
|
|
7
|
+
- Fixed role creation and renaming raising `ActiveRecord::RecordNotUnique` instead of returning `false` when called concurrently
|
|
8
|
+
- Fixed uniqueness of global and class context roles not being enforced at the database level
|
|
9
|
+
|
|
10
|
+
This release is fully backward compatible: existing applications continue to work as before without any changes. The new indexes only apply to new applications installing Rabarber. To get the same database-level protection in an existing application, run the following optional migration if your database supports partial indexes:
|
|
11
|
+
|
|
12
|
+
```rb
|
|
13
|
+
add_index :rabarber_roles, :name, unique: true, where: "context_type IS NULL AND context_id IS NULL"
|
|
14
|
+
add_index :rabarber_roles, [:name, :context_type], unique: true, where: "context_type IS NOT NULL AND context_id IS NULL"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
On databases without partial index support, such as MySQL, uniqueness of global and class context roles remains, for now, enforced at the application level only.
|
|
18
|
+
|
|
19
|
+
### Misc:
|
|
20
|
+
|
|
21
|
+
- `Rabarber.prune` now clears the role cache
|
|
22
|
+
|
|
23
|
+
## v6.0.0
|
|
24
|
+
|
|
25
|
+
### Breaking:
|
|
26
|
+
|
|
27
|
+
- Removed methods deprecated in v5.2.0 from the public API
|
|
28
|
+
- Dropped support for Ruby 3.2
|
|
29
|
+
- Dropped support for Rails 7.1
|
|
30
|
+
|
|
31
|
+
### Bugs:
|
|
32
|
+
|
|
33
|
+
- Fixed cache clearing not working with Memcached
|
|
34
|
+
|
|
35
|
+
### Misc:
|
|
36
|
+
|
|
37
|
+
- Optimized cache key generation
|
|
38
|
+
- Dropped some unnecessary dependencies
|
|
39
|
+
|
|
40
|
+
To upgrade to v6.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/101)
|
|
41
|
+
|
|
1
42
|
## v5.2.5
|
|
2
43
|
|
|
3
44
|
### Misc:
|
data/README.md
CHANGED
|
@@ -9,29 +9,23 @@ Rabarber is a role-based authorization library for Ruby on Rails. It provides a
|
|
|
9
9
|
|
|
10
10
|
**Example of Usage:**
|
|
11
11
|
|
|
12
|
-
Consider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can access invoice data
|
|
13
|
-
|
|
14
|
-
___
|
|
15
|
-
|
|
16
|
-
And this is how your controller might look with Rabarber:
|
|
12
|
+
Consider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can access and manage invoice data, while the role `analyst` can only view it. Here's how you'd define that with Rabarber:
|
|
17
13
|
|
|
18
14
|
```rb
|
|
19
|
-
class
|
|
20
|
-
grant_access roles: :
|
|
15
|
+
class InvoicesController < ApplicationController
|
|
16
|
+
grant_access roles: :accountant
|
|
21
17
|
|
|
22
|
-
grant_access action: :index, roles: :
|
|
18
|
+
grant_access action: :index, roles: :analyst
|
|
23
19
|
def index
|
|
24
|
-
#
|
|
20
|
+
# Accessible to accountant and analyst
|
|
25
21
|
end
|
|
26
22
|
|
|
27
|
-
def
|
|
28
|
-
#
|
|
23
|
+
def update
|
|
24
|
+
# Accessible to accountant only
|
|
29
25
|
end
|
|
30
26
|
end
|
|
31
27
|
```
|
|
32
28
|
|
|
33
|
-
This means that `admin` users can access everything in `TicketsController`, while the `manager` role can access only the `index` action.
|
|
34
|
-
|
|
35
29
|
## Table of Contents
|
|
36
30
|
|
|
37
31
|
**Gem Usage:**
|
|
@@ -87,9 +81,12 @@ Create an initializer to customize Rabarber's behavior (optional):
|
|
|
87
81
|
|
|
88
82
|
```rb
|
|
89
83
|
Rabarber.configure do |config|
|
|
90
|
-
|
|
91
|
-
config.
|
|
92
|
-
|
|
84
|
+
# Enable/disable role caching (default: true)
|
|
85
|
+
config.cache_enabled = true
|
|
86
|
+
# Method to access current user (default: :current_user)
|
|
87
|
+
config.current_user_method = :current_user
|
|
88
|
+
# User model name (default: "User")
|
|
89
|
+
config.user_model_name = "User"
|
|
93
90
|
end
|
|
94
91
|
```
|
|
95
92
|
|
|
@@ -119,7 +116,7 @@ user.revoke_roles(:admin, :manager)
|
|
|
119
116
|
user.revoke_all_roles
|
|
120
117
|
```
|
|
121
118
|
|
|
122
|
-
All role assignment methods return the list of roles currently assigned to the user.
|
|
119
|
+
All role assignment and revocation methods return the list of roles currently assigned to the user.
|
|
123
120
|
|
|
124
121
|
### Role Queries
|
|
125
122
|
|
|
@@ -139,19 +136,26 @@ User.with_role(:admin, :manager)
|
|
|
139
136
|
|
|
140
137
|
## Direct Role Management
|
|
141
138
|
|
|
142
|
-
You can also manage roles
|
|
139
|
+
You can also directly manage roles available in the application:
|
|
143
140
|
|
|
144
141
|
```rb
|
|
145
142
|
# Create a new role
|
|
146
|
-
Rabarber.create_role(:admin)
|
|
143
|
+
Rabarber.create_role(:admin)
|
|
144
|
+
# => true if created, false if already exists
|
|
147
145
|
|
|
148
146
|
# Rename a role
|
|
149
|
-
Rabarber.rename_role(:admin, :administrator)
|
|
150
|
-
|
|
147
|
+
Rabarber.rename_role(:admin, :administrator)
|
|
148
|
+
# => true if renamed, false if new name exists or role is assigned
|
|
149
|
+
|
|
150
|
+
# Force rename even if role is assigned
|
|
151
|
+
Rabarber.rename_role(:admin, :administrator, force: true)
|
|
151
152
|
|
|
152
153
|
# Remove a role
|
|
153
|
-
Rabarber.delete_role(:admin)
|
|
154
|
-
|
|
154
|
+
Rabarber.delete_role(:admin)
|
|
155
|
+
# => true if deleted, false if role is assigned
|
|
156
|
+
|
|
157
|
+
# Force deletion even if role is assigned
|
|
158
|
+
Rabarber.delete_role(:admin, force: true)
|
|
155
159
|
|
|
156
160
|
# List available roles in the global context
|
|
157
161
|
Rabarber.roles
|
|
@@ -160,19 +164,18 @@ Rabarber.roles
|
|
|
160
164
|
Rabarber.all_roles
|
|
161
165
|
```
|
|
162
166
|
|
|
163
|
-
> **Note:** Some methods have been deprecated in favor of the new API shown above. Deprecated methods still work but will be removed in a future major version. See the [changelog](https://github.com/enjaku4/rabarber/blob/main/CHANGELOG.md#v520) for the complete list of deprecated methods and their replacements.
|
|
164
|
-
|
|
165
167
|
## Authorization
|
|
166
168
|
|
|
167
169
|
### Setup
|
|
168
170
|
|
|
169
|
-
Include `Rabarber::Authorization` module in your controllers and configure
|
|
171
|
+
Include `Rabarber::Authorization` module in your controllers and configure authorization:
|
|
170
172
|
|
|
171
173
|
```rb
|
|
172
174
|
class ApplicationController < ActionController::Base
|
|
173
175
|
include Rabarber::Authorization
|
|
174
176
|
|
|
175
|
-
|
|
177
|
+
# Enable authorization check for all actions in all controllers by default
|
|
178
|
+
with_authorization
|
|
176
179
|
end
|
|
177
180
|
```
|
|
178
181
|
|
|
@@ -180,11 +183,13 @@ You can also enable authorization checks selectively. Both `with_authorization`
|
|
|
180
183
|
|
|
181
184
|
```rb
|
|
182
185
|
class TicketsController < ApplicationController
|
|
183
|
-
|
|
186
|
+
# Skip authorization for specific actions
|
|
187
|
+
skip_authorization only: [:index, :show]
|
|
184
188
|
end
|
|
185
189
|
|
|
186
190
|
class InvoicesController < ApplicationController
|
|
187
|
-
|
|
191
|
+
# Enable authorization for all actions except index
|
|
192
|
+
with_authorization except: [:index]
|
|
188
193
|
end
|
|
189
194
|
```
|
|
190
195
|
|
|
@@ -192,6 +197,8 @@ Authorization requires an authenticated user. Rabarber will raise an error if no
|
|
|
192
197
|
|
|
193
198
|
### Authorization Rules
|
|
194
199
|
|
|
200
|
+
Rabarber follows a deny-by-default principle: if no `grant_access` rule is defined for an action or controller, access is denied to everyone.
|
|
201
|
+
|
|
195
202
|
Define authorization rules using `grant_access`:
|
|
196
203
|
|
|
197
204
|
```rb
|
|
@@ -211,15 +218,17 @@ class TicketsController < ApplicationController
|
|
|
211
218
|
end
|
|
212
219
|
```
|
|
213
220
|
|
|
214
|
-
Authorization rules are additive -
|
|
221
|
+
Authorization rules are additive - rules defined multiple times combine, and controller-wide rules are inherited by subclasses:
|
|
215
222
|
|
|
216
223
|
```rb
|
|
217
224
|
class BaseController < ApplicationController
|
|
218
|
-
|
|
225
|
+
# Admin can access everything
|
|
226
|
+
grant_access roles: :admin
|
|
219
227
|
end
|
|
220
228
|
|
|
221
229
|
class InvoicesController < BaseController
|
|
222
|
-
|
|
230
|
+
# Accountant can also access InvoicesController (along with admin)
|
|
231
|
+
grant_access roles: :accountant
|
|
223
232
|
|
|
224
233
|
grant_access action: :index, roles: :manager
|
|
225
234
|
grant_access action: :index, roles: :supervisor
|
|
@@ -233,16 +242,19 @@ It's possible to omit roles to allow unrestricted access:
|
|
|
233
242
|
|
|
234
243
|
```rb
|
|
235
244
|
class UnrestrictedController < ApplicationController
|
|
236
|
-
|
|
245
|
+
# Allow all users to access all actions
|
|
246
|
+
grant_access
|
|
237
247
|
end
|
|
238
248
|
|
|
239
249
|
class MixedController < ApplicationController
|
|
240
|
-
|
|
250
|
+
# Unrestricted index action
|
|
251
|
+
grant_access action: :index
|
|
241
252
|
def index
|
|
242
253
|
# Accessible to all users
|
|
243
254
|
end
|
|
244
255
|
|
|
245
|
-
|
|
256
|
+
# Restricted show action
|
|
257
|
+
grant_access action: :show, roles: :member
|
|
246
258
|
def show
|
|
247
259
|
# Accessible to members only
|
|
248
260
|
end
|
|
@@ -276,7 +288,7 @@ Dynamic rules can also be used without roles at all, allowing you to define cust
|
|
|
276
288
|
|
|
277
289
|
```rb
|
|
278
290
|
class InvoicesController < ApplicationController
|
|
279
|
-
grant_access action: :update, unless: -> {
|
|
291
|
+
grant_access action: :update, unless: -> { invoice.period_closed? }
|
|
280
292
|
def update
|
|
281
293
|
# ...
|
|
282
294
|
end
|
|
@@ -289,7 +301,11 @@ class InvoicesController < ApplicationController
|
|
|
289
301
|
private
|
|
290
302
|
|
|
291
303
|
def destroy_allowed?
|
|
292
|
-
InvoicePolicy.new(current_user).destroy?(
|
|
304
|
+
InvoicePolicy.new(current_user).destroy?(invoice)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def invoice
|
|
308
|
+
@invoice ||= Invoice.find(params[:id])
|
|
293
309
|
end
|
|
294
310
|
end
|
|
295
311
|
```
|
|
@@ -307,7 +323,8 @@ class ApplicationController < ActionController::Base
|
|
|
307
323
|
private
|
|
308
324
|
|
|
309
325
|
def when_unauthorized
|
|
310
|
-
|
|
326
|
+
# Custom behavior to hide existence of protected resources
|
|
327
|
+
head :not_found
|
|
311
328
|
end
|
|
312
329
|
end
|
|
313
330
|
```
|
|
@@ -442,27 +459,31 @@ Use conditional rendering based on roles:
|
|
|
442
459
|
<% end %>
|
|
443
460
|
```
|
|
444
461
|
|
|
462
|
+
Like authorization, these helpers require an authenticated user. In views that can be rendered for both authenticated and unauthenticated visitors, wrap them in your authentication check.
|
|
463
|
+
|
|
445
464
|
## Getting Help and Contributing
|
|
446
465
|
|
|
447
466
|
### Getting Help
|
|
448
|
-
|
|
467
|
+
|
|
468
|
+
Have a question or need assistance? Open a discussion in the [discussions section](https://github.com/enjaku4/rabarber/discussions) for:
|
|
469
|
+
|
|
449
470
|
- Usage questions
|
|
450
471
|
- Implementation guidance
|
|
451
|
-
-
|
|
472
|
+
- Open-ended ideas or suggestions
|
|
473
|
+
|
|
474
|
+
### Issues
|
|
475
|
+
|
|
476
|
+
[Issues](https://github.com/enjaku4/rabarber/issues) track bugs, planned features, and other work. When reporting a bug, please include:
|
|
452
477
|
|
|
453
|
-
### Reporting Issues
|
|
454
|
-
Found a bug? Please [create an issue](https://github.com/enjaku4/rabarber/issues) with:
|
|
455
478
|
- A clear description of the problem
|
|
456
479
|
- Steps to reproduce the issue
|
|
457
|
-
- Your environment details (Rails
|
|
480
|
+
- Your environment details (Rails and Ruby versions, OS, etc.)
|
|
458
481
|
|
|
459
482
|
### Contributing Code
|
|
460
|
-
Ready to contribute? You can:
|
|
461
|
-
- Fix bugs by submitting pull requests
|
|
462
|
-
- Improve documentation
|
|
463
|
-
- Add new features (please discuss first in our [discussions section](https://github.com/enjaku4/rabarber/discussions))
|
|
464
483
|
|
|
465
|
-
|
|
484
|
+
Any open issue is free to pick up or discuss. Check the [project board](https://github.com/users/enjaku4/projects/10) or [issues tab](https://github.com/enjaku4/rabarber/issues).
|
|
485
|
+
|
|
486
|
+
Before contributing, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md).
|
|
466
487
|
|
|
467
488
|
## License
|
|
468
489
|
|
|
@@ -474,7 +495,7 @@ Everyone interacting in the Rabarber project is expected to follow the [code of
|
|
|
474
495
|
|
|
475
496
|
## Old Versions
|
|
476
497
|
|
|
477
|
-
Only the latest major version is
|
|
498
|
+
Only the latest major version is actively maintained. READMEs for older versions are available here for reference:
|
|
478
499
|
|
|
479
|
-
[v4.x.x](https://github.com/enjaku4/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/enjaku4/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
|
|
500
|
+
[v5.x.x](https://github.com/enjaku4/rabarber/blob/12a23858e974f5cc0a4ebddfc18bdf84171dd554/README.md) | [v4.x.x](https://github.com/enjaku4/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/enjaku4/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
|
|
480
501
|
| [v2.x.x](https://github.com/enjaku4/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/enjaku4/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)
|
|
@@ -10,6 +10,11 @@ class CreateRabarberRoles < ActiveRecord::Migration[<%= ActiveRecord::Migration.
|
|
|
10
10
|
|
|
11
11
|
add_index :rabarber_roles, [:name, :context_type, :context_id], unique: true
|
|
12
12
|
|
|
13
|
+
if connection.supports_partial_index?
|
|
14
|
+
add_index :rabarber_roles, :name, unique: true, where: "context_type IS NULL AND context_id IS NULL"
|
|
15
|
+
add_index :rabarber_roles, [:name, :context_type], unique: true, where: "context_type IS NOT NULL AND context_id IS NULL"
|
|
16
|
+
end
|
|
17
|
+
|
|
13
18
|
create_table :rabarber_roles_roleables, id: false do |t|
|
|
14
19
|
t.belongs_to :role, null: false, index: true, foreign_key: { to_table: :rabarber_roles }<%= ", type: :uuid" if options[:uuid] %>
|
|
15
20
|
t.belongs_to :roleable, null: false, index: true, foreign_key: { to_table: <%= table_name.to_sym.inspect %> }<%= ", type: :uuid" if options[:uuid] %>
|
|
@@ -1,43 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "singleton"
|
|
4
4
|
|
|
5
5
|
module Rabarber
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
6
|
+
class Configuration
|
|
7
|
+
include Singleton
|
|
8
|
+
|
|
9
|
+
attr_reader :cache_enabled, :current_user_method, :user_model_name
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
reset_to_defaults!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def cache_enabled=(value)
|
|
16
|
+
@cache_enabled = Rabarber::Inputs::Boolean.new(
|
|
17
|
+
value,
|
|
18
|
+
error: Rabarber::ConfigurationError,
|
|
19
|
+
message: "Invalid configuration `cache_enabled`, expected a boolean, got #{value.inspect}"
|
|
20
|
+
).process
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def current_user_method=(value)
|
|
24
|
+
@current_user_method = Rabarber::Inputs::NonEmptySymbol.new(
|
|
25
|
+
value,
|
|
26
|
+
error: Rabarber::ConfigurationError,
|
|
27
|
+
message: "Invalid configuration `current_user_method`, expected a symbol or a string, got #{value.inspect}"
|
|
28
|
+
).process
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def user_model_name=(value)
|
|
32
|
+
@user_model_name = Rabarber::Inputs::NonEmptyString.new(
|
|
33
|
+
value,
|
|
34
|
+
error: Rabarber::ConfigurationError,
|
|
35
|
+
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{value.inspect}"
|
|
36
|
+
).process
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def reset_to_defaults!
|
|
40
|
+
self.cache_enabled = true
|
|
41
|
+
self.current_user_method = :current_user
|
|
42
|
+
self.user_model_name = "User"
|
|
43
|
+
end
|
|
41
44
|
|
|
42
45
|
def user_model
|
|
43
46
|
Rabarber::Inputs::Model.new(
|
|
@@ -46,5 +49,16 @@ module Rabarber
|
|
|
46
49
|
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{user_model_name.inspect}"
|
|
47
50
|
).process
|
|
48
51
|
end
|
|
52
|
+
|
|
53
|
+
def configure
|
|
54
|
+
yield self
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class << self
|
|
58
|
+
delegate :cache_enabled, :current_user_method, :user_model_name,
|
|
59
|
+
:cache_enabled=, :current_user_method=, :user_model_name=,
|
|
60
|
+
:user_model, :reset_to_defaults!, :configure,
|
|
61
|
+
to: :instance
|
|
62
|
+
end
|
|
49
63
|
end
|
|
50
64
|
end
|
data/lib/rabarber/core/cache.rb
CHANGED
|
@@ -1,38 +1,54 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "digest/
|
|
3
|
+
require "digest/md5"
|
|
4
|
+
require "securerandom"
|
|
4
5
|
|
|
5
6
|
module Rabarber
|
|
6
7
|
module Core
|
|
7
8
|
module Cache
|
|
8
|
-
|
|
9
|
+
extend self
|
|
9
10
|
|
|
10
|
-
def fetch(
|
|
11
|
+
def fetch(roleable_id, scope, &)
|
|
11
12
|
return yield unless enabled?
|
|
12
13
|
|
|
13
|
-
Rails.cache.fetch(prepare_key(
|
|
14
|
+
Rails.cache.fetch(prepare_key(roleable_id, scope), expires_in: 1.hour, race_condition_ttl: 5.seconds, &)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def delete(*
|
|
17
|
-
return unless enabled?
|
|
17
|
+
def delete(*pairs)
|
|
18
|
+
return unless enabled? && pairs.any?
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
keys = pairs.map { |roleable_id, scope| prepare_key(roleable_id, scope) }
|
|
21
|
+
|
|
22
|
+
Rails.cache.delete_multi(keys)
|
|
23
|
+
ActiveRecord.after_all_transactions_commit { Rails.cache.delete_multi(keys) } if ActiveRecord::Base.connection.transaction_open?
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
def clear
|
|
27
|
+
Rails.cache.write(VERSION_KEY, SecureRandom.alphanumeric(8))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
22
32
|
def enabled?
|
|
23
33
|
Rabarber::Configuration.cache_enabled
|
|
24
34
|
end
|
|
25
35
|
|
|
26
|
-
def
|
|
27
|
-
|
|
36
|
+
def prepare_key(roleable_id, scope)
|
|
37
|
+
Digest::MD5.base64digest(Marshal.dump([current_version, roleable_id, scope]))
|
|
28
38
|
end
|
|
29
39
|
|
|
30
|
-
def
|
|
31
|
-
|
|
40
|
+
def current_version
|
|
41
|
+
version = Rails.cache.read(VERSION_KEY).presence
|
|
42
|
+
|
|
43
|
+
return version if version
|
|
44
|
+
|
|
45
|
+
clear
|
|
46
|
+
|
|
47
|
+
Rails.cache.read(VERSION_KEY)
|
|
32
48
|
end
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
private_constant :
|
|
50
|
+
VERSION_KEY = "rabarber"
|
|
51
|
+
private_constant :VERSION_KEY
|
|
36
52
|
end
|
|
37
53
|
end
|
|
38
54
|
|
data/lib/rabarber/inputs/base.rb
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "dry-types"
|
|
4
|
-
|
|
5
3
|
module Rabarber
|
|
6
4
|
module Inputs
|
|
7
5
|
class Base
|
|
8
|
-
include Dry.Types()
|
|
9
|
-
|
|
10
6
|
def initialize(value, optional: false, error: Rabarber::InvalidArgumentError, message: nil)
|
|
11
7
|
@value = value
|
|
12
8
|
@optional = optional
|
|
@@ -15,15 +11,14 @@ module Rabarber
|
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def process
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
raise_error
|
|
14
|
+
return @value if @value.nil? && @optional
|
|
15
|
+
|
|
16
|
+
validate_and_normalize
|
|
22
17
|
end
|
|
23
18
|
|
|
24
19
|
private
|
|
25
20
|
|
|
26
|
-
def
|
|
21
|
+
def validate_and_normalize
|
|
27
22
|
raise NotImplementedError
|
|
28
23
|
end
|
|
29
24
|
|
|
@@ -19,14 +19,13 @@ module Rabarber
|
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
self.class::Nil
|
|
22
|
+
def validate_and_normalize
|
|
23
|
+
return @value if @value.nil?
|
|
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 }
|
|
27
|
+
|
|
28
|
+
raise_error
|
|
30
29
|
end
|
|
31
30
|
end
|
|
32
31
|
end
|
|
@@ -4,9 +4,21 @@ 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
|
+
|
|
7
14
|
private
|
|
8
15
|
|
|
9
|
-
def
|
|
16
|
+
def validate_and_normalize
|
|
17
|
+
return @value if @value.is_a?(Proc)
|
|
18
|
+
return @value.to_sym if (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.present?
|
|
19
|
+
|
|
20
|
+
super
|
|
21
|
+
end
|
|
10
22
|
end
|
|
11
23
|
end
|
|
12
24
|
end
|
|
@@ -5,7 +5,11 @@ module Rabarber
|
|
|
5
5
|
class DynamicRule < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
return @value if @value.is_a?(Proc)
|
|
10
|
+
|
|
11
|
+
Rabarber::Inputs::NonEmptySymbol.new(@value, error: @error, message: @message).process
|
|
12
|
+
end
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
end
|
|
@@ -5,7 +5,12 @@ module Rabarber
|
|
|
5
5
|
class Model < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
model = @value.try(:safe_constantize)
|
|
10
|
+
raise_error unless model.is_a?(Class) && model < ActiveRecord::Base
|
|
11
|
+
|
|
12
|
+
model
|
|
13
|
+
end
|
|
9
14
|
end
|
|
10
15
|
end
|
|
11
16
|
end
|
|
@@ -5,7 +5,9 @@ module Rabarber
|
|
|
5
5
|
class NonEmptyString < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
@value.is_a?(String) && @value.present? ? @value : raise_error
|
|
10
|
+
end
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rabarber
|
|
4
|
+
module Inputs
|
|
5
|
+
class NonEmptySymbol < Rabarber::Inputs::Base
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
raise_error unless (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.present?
|
|
10
|
+
|
|
11
|
+
@value.to_sym
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/rabarber/inputs/role.rb
CHANGED
|
@@ -5,7 +5,11 @@ module Rabarber
|
|
|
5
5
|
class Role < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
raise_error unless (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.to_s.match?(/\A[a-z0-9_]+\z/)
|
|
10
|
+
|
|
11
|
+
@value.to_sym
|
|
12
|
+
end
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
end
|
|
@@ -5,8 +5,10 @@ module Rabarber
|
|
|
5
5
|
class Roles < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
9
|
-
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
Array(@value).map do |role|
|
|
10
|
+
Rabarber::Inputs::Role.new(role, error: @error, message: @message).process
|
|
11
|
+
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
12
14
|
end
|
|
@@ -40,17 +40,17 @@ module Rabarber
|
|
|
40
40
|
|
|
41
41
|
def roles(context: nil)
|
|
42
42
|
processed_context = process_context(context)
|
|
43
|
-
Rabarber::Core::Cache.fetch(
|
|
43
|
+
Rabarber::Core::Cache.fetch(roleable_id, processed_context) { rabarber_roles.list(context: processed_context) }
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def all_roles
|
|
47
|
-
Rabarber::Core::Cache.fetch(
|
|
47
|
+
Rabarber::Core::Cache.fetch(roleable_id, :all) { rabarber_roles.list_all }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def has_role?(*role_names, context: nil)
|
|
51
51
|
processed_context = process_context(context)
|
|
52
52
|
processed_roles = process_role_names(role_names)
|
|
53
|
-
roles(context: processed_context).
|
|
53
|
+
roles(context: processed_context).intersect?(processed_roles)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def assign_roles(*role_names, context: nil, create_new: true)
|
|
@@ -60,12 +60,16 @@ module Rabarber
|
|
|
60
60
|
create_new_roles(processed_role_names, context: processed_context) if create_new
|
|
61
61
|
|
|
62
62
|
roles_to_assign = Rabarber::Role.where(
|
|
63
|
-
name: (processed_role_names - rabarber_roles.
|
|
63
|
+
name: (processed_role_names - rabarber_roles.list(context: processed_context)), **processed_context
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
if roles_to_assign.any?
|
|
67
|
+
begin
|
|
68
|
+
rabarber_roles << roles_to_assign
|
|
69
|
+
rescue ActiveRecord::RecordNotUnique
|
|
70
|
+
rabarber_roles.reset
|
|
71
|
+
end
|
|
67
72
|
delete_roleable_cache(contexts: [processed_context])
|
|
68
|
-
rabarber_roles << roles_to_assign
|
|
69
73
|
end
|
|
70
74
|
|
|
71
75
|
roles(context: processed_context)
|
|
@@ -76,12 +80,12 @@ module Rabarber
|
|
|
76
80
|
processed_context = process_context(context)
|
|
77
81
|
|
|
78
82
|
roles_to_revoke = Rabarber::Role.where(
|
|
79
|
-
name: processed_role_names.intersection(rabarber_roles.
|
|
83
|
+
name: processed_role_names.intersection(rabarber_roles.list(context: processed_context)), **processed_context
|
|
80
84
|
)
|
|
81
85
|
|
|
82
86
|
if roles_to_revoke.any?
|
|
83
|
-
delete_roleable_cache(contexts: [processed_context])
|
|
84
87
|
self.rabarber_roles -= roles_to_revoke
|
|
88
|
+
delete_roleable_cache(contexts: [processed_context])
|
|
85
89
|
end
|
|
86
90
|
|
|
87
91
|
roles(context: processed_context)
|
|
@@ -103,7 +107,7 @@ module Rabarber
|
|
|
103
107
|
|
|
104
108
|
def create_new_roles(role_names, context:)
|
|
105
109
|
new_roles = role_names - Rabarber.roles(context:)
|
|
106
|
-
new_roles.each { |role_name| Rabarber::Role.
|
|
110
|
+
new_roles.each { |role_name| Rabarber::Role.register(role_name, context:) }
|
|
107
111
|
end
|
|
108
112
|
|
|
109
113
|
def delete_roleable_cache(contexts:)
|
data/lib/rabarber/models/role.rb
CHANGED
|
@@ -6,20 +6,12 @@ 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.user_model_name,
|
|
10
|
-
association_foreign_key: "roleable_id",
|
|
11
|
-
join_table: "rabarber_roles_roleables"
|
|
12
|
-
|
|
13
9
|
class << self
|
|
14
|
-
def
|
|
15
|
-
deprecation_warning("names", "Rabarber.roles")
|
|
16
|
-
|
|
10
|
+
def list(context: nil)
|
|
17
11
|
where(process_context(context)).pluck(:name).map(&:to_sym)
|
|
18
12
|
end
|
|
19
13
|
|
|
20
|
-
def
|
|
21
|
-
deprecation_warning("all_names", "Rabarber.all_roles")
|
|
22
|
-
|
|
14
|
+
def list_all
|
|
23
15
|
includes(:context).each_with_object({}) do |role, hash|
|
|
24
16
|
(hash[role.context] ||= []) << role.name.to_sym
|
|
25
17
|
rescue ActiveRecord::RecordNotFound
|
|
@@ -29,20 +21,18 @@ module Rabarber
|
|
|
29
21
|
raise Rabarber::NotFoundError, "Context not found: class #{e.name} may have been renamed or deleted"
|
|
30
22
|
end
|
|
31
23
|
|
|
32
|
-
def
|
|
33
|
-
deprecation_warning("add", "Rabarber.create_role")
|
|
34
|
-
|
|
24
|
+
def register(name, context: nil)
|
|
35
25
|
name = process_role_name(name)
|
|
36
26
|
processed_context = process_context(context)
|
|
37
27
|
|
|
38
28
|
return false if exists?(name:, **processed_context)
|
|
39
29
|
|
|
40
30
|
!!create!(name:, **processed_context)
|
|
31
|
+
rescue ActiveRecord::RecordNotUnique
|
|
32
|
+
false
|
|
41
33
|
end
|
|
42
34
|
|
|
43
|
-
def
|
|
44
|
-
deprecation_warning("rename", "Rabarber.rename_role")
|
|
45
|
-
|
|
35
|
+
def amend(old_name, new_name, context: nil, force: false)
|
|
46
36
|
processed_context = process_context(context)
|
|
47
37
|
role = find_by(name: process_role_name(old_name), **processed_context)
|
|
48
38
|
|
|
@@ -52,14 +42,16 @@ module Rabarber
|
|
|
52
42
|
|
|
53
43
|
return false if exists?(name:, **processed_context) || role.roleables.exists? && !force
|
|
54
44
|
|
|
55
|
-
delete_roleables_cache(role, context: processed_context)
|
|
56
|
-
|
|
57
45
|
role.update!(name:)
|
|
58
|
-
end
|
|
59
46
|
|
|
60
|
-
|
|
61
|
-
deprecation_warning("remove", "Rabarber.delete_role")
|
|
47
|
+
delete_roleables_cache(role.roleables.pluck(:id), context: processed_context)
|
|
62
48
|
|
|
49
|
+
true
|
|
50
|
+
rescue ActiveRecord::RecordNotUnique
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def drop(name, context: nil, force: false)
|
|
63
55
|
processed_context = process_context(context)
|
|
64
56
|
role = find_by(name: process_role_name(name), **processed_context)
|
|
65
57
|
|
|
@@ -67,15 +59,13 @@ module Rabarber
|
|
|
67
59
|
|
|
68
60
|
return false if role.roleables.exists? && !force
|
|
69
61
|
|
|
70
|
-
|
|
62
|
+
roleable_ids = role.roleables.pluck(:id)
|
|
71
63
|
|
|
72
|
-
|
|
73
|
-
end
|
|
64
|
+
role.destroy!
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
deprecation_warning("assignees", "#{Rabarber::Configuration.user_model_name}.with_role")
|
|
66
|
+
delete_roleables_cache(roleable_ids, context: processed_context)
|
|
77
67
|
|
|
78
|
-
|
|
68
|
+
true
|
|
79
69
|
end
|
|
80
70
|
|
|
81
71
|
def prune
|
|
@@ -95,20 +85,14 @@ module Rabarber
|
|
|
95
85
|
)
|
|
96
86
|
where(id: orphaned_roles).delete_all
|
|
97
87
|
end
|
|
88
|
+
|
|
89
|
+
Rabarber::Core::Cache.clear
|
|
98
90
|
end
|
|
99
91
|
|
|
100
92
|
private
|
|
101
93
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return if callers.include?(alternative) || callers.include?("ActiveRecord::Relation#scoping")
|
|
106
|
-
|
|
107
|
-
ActiveSupport::Deprecation.new("6.0.0", "rabarber").warn("`Rabarber::Role.#{method}` method is deprecated and will be removed in the next major version, use `#{alternative}` instead.")
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def delete_roleables_cache(role, context:)
|
|
111
|
-
Rabarber::Core::Cache.delete(*role.roleables.pluck(:id).flat_map { [[_1, context], [_1, :all]] })
|
|
94
|
+
def delete_roleables_cache(roleable_ids, context:)
|
|
95
|
+
Rabarber::Core::Cache.delete(*roleable_ids.flat_map { [[_1, context], [_1, :all]] })
|
|
112
96
|
end
|
|
113
97
|
|
|
114
98
|
def process_role_name(name)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rabarber
|
|
4
|
+
module RoleManagement
|
|
5
|
+
def roles(context: nil) = Rabarber::Role.list(context:)
|
|
6
|
+
def all_roles = Rabarber::Role.list_all
|
|
7
|
+
def create_role(name, context: nil) = Rabarber::Role.register(name, context:)
|
|
8
|
+
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.drop(name, context:, force:)
|
|
10
|
+
def prune = Rabarber::Role.prune # rubocop:disable Rails/Delegate
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/rabarber/railtie.rb
CHANGED
|
@@ -28,6 +28,11 @@ module Rabarber
|
|
|
28
28
|
|
|
29
29
|
user_model = Rabarber::Configuration.user_model
|
|
30
30
|
user_model.include Rabarber::Roleable unless user_model < Rabarber::Roleable
|
|
31
|
+
|
|
32
|
+
Rabarber::Role.send(:remove_const, :HABTM_Roleables) if Rabarber::Role.const_defined?(:HABTM_Roleables, false)
|
|
33
|
+
Rabarber::Role.has_and_belongs_to_many :roleables, class_name: Rabarber::Configuration.user_model_name,
|
|
34
|
+
association_foreign_key: "roleable_id",
|
|
35
|
+
join_table: "rabarber_roles_roleables"
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
|
data/lib/rabarber/version.rb
CHANGED
data/lib/rabarber.rb
CHANGED
|
@@ -14,7 +14,7 @@ require_relative "rabarber/inputs/model"
|
|
|
14
14
|
require_relative "rabarber/inputs/non_empty_string"
|
|
15
15
|
require_relative "rabarber/inputs/role"
|
|
16
16
|
require_relative "rabarber/inputs/roles"
|
|
17
|
-
require_relative "rabarber/inputs/
|
|
17
|
+
require_relative "rabarber/inputs/non_empty_symbol"
|
|
18
18
|
|
|
19
19
|
require_relative "rabarber/configuration"
|
|
20
20
|
|
|
@@ -27,15 +27,6 @@ module Rabarber
|
|
|
27
27
|
|
|
28
28
|
delegate :configure, to: Rabarber::Configuration
|
|
29
29
|
module_function :configure
|
|
30
|
-
|
|
31
|
-
class << self
|
|
32
|
-
def roles(context: nil) = Rabarber::Role.names(context:)
|
|
33
|
-
def all_roles = Rabarber::Role.all_names
|
|
34
|
-
def create_role(name, context: nil) = Rabarber::Role.add(name, context:)
|
|
35
|
-
def rename_role(old_name, new_name, context: nil, force: false) = Rabarber::Role.rename(old_name, new_name, context:, force:)
|
|
36
|
-
def delete_role(name, context: nil, force: false) = Rabarber::Role.remove(name, context:, force:)
|
|
37
|
-
def prune = Rabarber::Role.prune
|
|
38
|
-
end
|
|
39
30
|
end
|
|
40
31
|
|
|
41
32
|
require_relative "rabarber/core/cache"
|
|
@@ -47,6 +38,9 @@ require_relative "rabarber/helpers/helpers"
|
|
|
47
38
|
require_relative "rabarber/helpers/migration_helpers"
|
|
48
39
|
require_relative "rabarber/models/concerns/roleable"
|
|
49
40
|
require_relative "rabarber/models/role"
|
|
41
|
+
require_relative "rabarber/models/role_management"
|
|
42
|
+
|
|
43
|
+
Rabarber.extend Rabarber::RoleManagement
|
|
50
44
|
|
|
51
45
|
require_relative "rabarber/core/permissions"
|
|
52
46
|
|
data/rabarber.gemspec
CHANGED
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.summary = "Simple role-based authorization library for Ruby on Rails"
|
|
20
20
|
spec.description = "Simple role-based authorization for Rails applications with multi-tenancy support"
|
|
21
21
|
spec.license = "MIT"
|
|
22
|
-
spec.required_ruby_version = ">= 3.
|
|
22
|
+
spec.required_ruby_version = ">= 3.3", "< 4.1"
|
|
23
23
|
|
|
24
24
|
spec.files = [
|
|
25
25
|
"rabarber.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"
|
|
@@ -27,7 +27,5 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
|
|
28
28
|
spec.require_paths = ["lib"]
|
|
29
29
|
|
|
30
|
-
spec.add_dependency "
|
|
31
|
-
spec.add_dependency "dry-types", "~> 1.7"
|
|
32
|
-
spec.add_dependency "rails", ">= 7.1", "< 8.2"
|
|
30
|
+
spec.add_dependency "rails", ">= 7.2", "< 8.2"
|
|
33
31
|
end
|
metadata
CHANGED
|
@@ -1,50 +1,22 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rabarber
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.0.1
|
|
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: 2026-07-19 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.1'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.1'
|
|
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.7'
|
|
34
|
-
type: :runtime
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '1.7'
|
|
41
13
|
- !ruby/object:Gem::Dependency
|
|
42
14
|
name: rails
|
|
43
15
|
requirement: !ruby/object:Gem::Requirement
|
|
44
16
|
requirements:
|
|
45
17
|
- - ">="
|
|
46
18
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '7.
|
|
19
|
+
version: '7.2'
|
|
48
20
|
- - "<"
|
|
49
21
|
- !ruby/object:Gem::Version
|
|
50
22
|
version: '8.2'
|
|
@@ -54,7 +26,7 @@ dependencies:
|
|
|
54
26
|
requirements:
|
|
55
27
|
- - ">="
|
|
56
28
|
- !ruby/object:Gem::Version
|
|
57
|
-
version: '7.
|
|
29
|
+
version: '7.2'
|
|
58
30
|
- - "<"
|
|
59
31
|
- !ruby/object:Gem::Version
|
|
60
32
|
version: '8.2'
|
|
@@ -88,11 +60,12 @@ files:
|
|
|
88
60
|
- lib/rabarber/inputs/dynamic_rule.rb
|
|
89
61
|
- lib/rabarber/inputs/model.rb
|
|
90
62
|
- lib/rabarber/inputs/non_empty_string.rb
|
|
63
|
+
- lib/rabarber/inputs/non_empty_symbol.rb
|
|
91
64
|
- lib/rabarber/inputs/role.rb
|
|
92
65
|
- lib/rabarber/inputs/roles.rb
|
|
93
|
-
- lib/rabarber/inputs/symbol.rb
|
|
94
66
|
- lib/rabarber/models/concerns/roleable.rb
|
|
95
67
|
- lib/rabarber/models/role.rb
|
|
68
|
+
- lib/rabarber/models/role_management.rb
|
|
96
69
|
- lib/rabarber/railtie.rb
|
|
97
70
|
- lib/rabarber/version.rb
|
|
98
71
|
- rabarber.gemspec
|
|
@@ -115,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
115
88
|
requirements:
|
|
116
89
|
- - ">="
|
|
117
90
|
- !ruby/object:Gem::Version
|
|
118
|
-
version: '3.
|
|
91
|
+
version: '3.3'
|
|
119
92
|
- - "<"
|
|
120
93
|
- !ruby/object:Gem::Version
|
|
121
94
|
version: '4.1'
|
|
@@ -125,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
98
|
- !ruby/object:Gem::Version
|
|
126
99
|
version: '0'
|
|
127
100
|
requirements: []
|
|
128
|
-
rubygems_version:
|
|
101
|
+
rubygems_version: 4.0.15
|
|
129
102
|
specification_version: 4
|
|
130
103
|
summary: Simple role-based authorization library for Ruby on Rails
|
|
131
104
|
test_files: []
|