rabarber 1.0.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -2
- data/README.md +12 -21
- data/lib/rabarber/configuration.rb +3 -5
- data/lib/rabarber/controllers/concerns/authorization.rb +1 -1
- data/lib/rabarber/models/concerns/has_roles.rb +13 -11
- data/lib/rabarber/models/role.rb +3 -1
- data/lib/rabarber/rule.rb +28 -3
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +2 -0
- data/rabarber.gemspec +23 -0
- metadata +3 -6
- data/.rspec +0 -4
- data/.rubocop.yml +0 -69
- data/Gemfile +0 -17
- data/Rakefile +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f83c97e518621ea7749eab381d5e1e4f84785c8197036e923220fa0d29fcc43e
|
4
|
+
data.tar.gz: 9fe75afecf6c3066c0cbf779ffdf79a2e67716a8acdc0f15c72e85008528f5ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 407ed9c374cbe8dafd964aa939f342d47a307bd101e2932bf8ae12c526f48a75524df6eb6f6f35a6a3fb4a023f7db5e50a7c04b580f56fde06ccfcc09f431df8
|
7
|
+
data.tar.gz: 4cfcde7d7b6ed23faff8d0e6f8f036c641ecf6dfb18e1b075dbdfd6aaa5f8391dfdf13ea7e25b70b7881489fc3b292791f663c6c70b1a32a634e7caf681a2355
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
+
## 1.0.3
|
2
|
+
|
3
|
+
- Enhance clarity by improving error types and messages
|
4
|
+
- Resolve inconsistency in types of role names
|
5
|
+
|
6
|
+
## 1.0.2
|
7
|
+
|
8
|
+
- Various enhancements for gem development and release
|
9
|
+
- Modify `HasRoles#roles` method to return an array of role names instead of `Rabarber::Role` objects
|
10
|
+
|
1
11
|
## 1.0.1
|
2
12
|
|
3
|
-
- Various
|
13
|
+
- Various enhancements for gem development
|
4
14
|
|
5
15
|
## 1.0.0
|
6
16
|
|
@@ -15,7 +25,7 @@
|
|
15
25
|
|
16
26
|
## 0.1.4
|
17
27
|
|
18
|
-
- Remove `role?` method as unnecessary
|
28
|
+
- Remove `HasRoles#role?` method as unnecessary
|
19
29
|
|
20
30
|
## 0.1.3
|
21
31
|
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Rabarber is an authorization library for Ruby on Rails, primarily designed for u
|
|
7
7
|
|
8
8
|
---
|
9
9
|
|
10
|
-
|
10
|
+
**Example of Usage**:
|
11
11
|
|
12
12
|
Consider a CRM where users with different roles have distinct access levels. For instance, the role 'accountant' can interact with invoices but cannot access marketing information, while the role 'marketer' has access to marketing-related data. Such authorization rules can be easily defined with Rabarber.
|
13
13
|
|
@@ -78,7 +78,7 @@ end
|
|
78
78
|
- `must_have_roles` must be a boolean determining whether a user with no roles can access endpoints permitted to everyone. The default value is `false` (allowing users without roles to access endpoints permitted to everyone).
|
79
79
|
- `when_unauthorized` must be a lambda where you can define your actions when access is not authorized (`controller` is an instance of the controller where the code is executed). By default, the user is redirected back if the request format is HTML; otherwise, a 401 Unauthorized response is sent.
|
80
80
|
|
81
|
-
##
|
81
|
+
## Roles
|
82
82
|
|
83
83
|
Include `Rabarber::HasRoles` module in your model representing users in your application:
|
84
84
|
|
@@ -89,9 +89,9 @@ class User < ApplicationRecord
|
|
89
89
|
end
|
90
90
|
```
|
91
91
|
|
92
|
-
|
92
|
+
This adds the following methods:
|
93
93
|
|
94
|
-
|
94
|
+
**`#assign_roles`**
|
95
95
|
|
96
96
|
To assign roles to the user, use:
|
97
97
|
|
@@ -110,7 +110,7 @@ Rabarber::Role.create(name: "manager")
|
|
110
110
|
```
|
111
111
|
The role names are unique.
|
112
112
|
|
113
|
-
|
113
|
+
**`#revoke_roles`**
|
114
114
|
|
115
115
|
To revoke roles, use:
|
116
116
|
|
@@ -119,7 +119,7 @@ user.revoke_roles(:accountant, :marketer)
|
|
119
119
|
```
|
120
120
|
If any of the specified roles doesn't exist or the user doesn't have the role you want to revoke, it will be ignored.
|
121
121
|
|
122
|
-
|
122
|
+
**`#has_role?`**
|
123
123
|
|
124
124
|
To check whether the user has a role, use:
|
125
125
|
|
@@ -129,20 +129,13 @@ user.has_role?(:accountant, :marketer)
|
|
129
129
|
|
130
130
|
It returns `true` if the user has at least one role and `false` otherwise.
|
131
131
|
|
132
|
-
|
132
|
+
**`#roles`**
|
133
133
|
|
134
134
|
To view all the roles assigned to the user, use:
|
135
135
|
|
136
136
|
```rb
|
137
137
|
user.roles
|
138
138
|
```
|
139
|
-
This will return an array of `Rabarber::Role` objects.
|
140
|
-
|
141
|
-
If you need the list of role names, use:
|
142
|
-
|
143
|
-
```rb
|
144
|
-
user.roles.names
|
145
|
-
```
|
146
139
|
|
147
140
|
If you need to list all the role names available in your application, use:
|
148
141
|
|
@@ -150,11 +143,11 @@ If you need to list all the role names available in your application, use:
|
|
150
143
|
Rabarber::Role.names
|
151
144
|
```
|
152
145
|
|
153
|
-
|
146
|
+
`Rabarber::Role` is a model that represents roles within your application. It has a single attribute, `name`, which is validated for both uniqueness and presence. You can treat `Rabarber::Role` as a regular Rails model and use Active Record methods on it if necessary.
|
154
147
|
|
155
|
-
|
148
|
+
Utilize the aforementioned methods to manipulate user roles. For example, create a custom UI for managing roles or assign necessary roles during migration or runtime (e.g., when the user is created). You can also write custom authorization policies based on `#has_role?` method (e.g., to scope the data that the user can access). Adapt these methods to fit the requirements of your application.
|
156
149
|
|
157
|
-
|
150
|
+
## Authorization Rules
|
158
151
|
|
159
152
|
Include `Rabarber::Authorization` module into the controller that needs authorization rules to be applied (authorization rules will be applied to the controller and its children). Typically, it is `ApplicationController`, but it can be any controller.
|
160
153
|
|
@@ -164,7 +157,7 @@ class ApplicationController < ActionController::Base
|
|
164
157
|
...
|
165
158
|
end
|
166
159
|
```
|
167
|
-
This adds `.grant_access` method
|
160
|
+
This adds `.grant_access` method which allows you to define the authorization rules.
|
168
161
|
|
169
162
|
The most basic usage of the method is as follows:
|
170
163
|
|
@@ -265,9 +258,7 @@ end
|
|
265
258
|
```
|
266
259
|
This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
|
267
260
|
|
268
|
-
|
269
|
-
|
270
|
-
### View Helpers
|
261
|
+
## View Helpers
|
271
262
|
|
272
263
|
Rabarber also provides a couple of helpers that can be used in views: `visible_to` and `hidden_from`. The usage is straightforward:
|
273
264
|
|
@@ -21,21 +21,19 @@ module Rabarber
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def current_user_method=(method_name)
|
24
|
-
|
25
|
-
raise ArgumentError, "Method name must be a symbol or a string"
|
26
|
-
end
|
24
|
+
raise ConfigurationError, "current_user_method must be a symbol" unless method_name.is_a?(Symbol)
|
27
25
|
|
28
26
|
@current_user_method = method_name.to_sym
|
29
27
|
end
|
30
28
|
|
31
29
|
def must_have_roles=(value)
|
32
|
-
raise
|
30
|
+
raise ConfigurationError, "must_have_roles must be a boolean" unless [true, false].include?(value)
|
33
31
|
|
34
32
|
@must_have_roles = value
|
35
33
|
end
|
36
34
|
|
37
35
|
def when_unauthorized=(callable)
|
38
|
-
raise
|
36
|
+
raise ConfigurationError, "when_unauthorized must be a proc" unless callable.is_a?(Proc)
|
39
37
|
|
40
38
|
@when_unauthorized = callable
|
41
39
|
end
|
@@ -18,7 +18,7 @@ module Rabarber
|
|
18
18
|
|
19
19
|
def verify_access
|
20
20
|
return if Permissions.access_granted?(
|
21
|
-
send(::Rabarber::Configuration.instance.current_user_method).roles
|
21
|
+
send(::Rabarber::Configuration.instance.current_user_method).roles, self.class, action_name.to_sym, self
|
22
22
|
)
|
23
23
|
|
24
24
|
::Rabarber::Configuration.instance.when_unauthorized.call(self)
|
@@ -9,15 +9,19 @@ module Rabarber
|
|
9
9
|
|
10
10
|
@@included = name
|
11
11
|
|
12
|
-
has_and_belongs_to_many :
|
13
|
-
|
14
|
-
|
12
|
+
has_and_belongs_to_many :rabarber_roles, class_name: "Rabarber::Role",
|
13
|
+
foreign_key: "roleable_id",
|
14
|
+
join_table: "rabarber_roles_roleables"
|
15
|
+
end
|
16
|
+
|
17
|
+
def roles
|
18
|
+
rabarber_roles.names
|
15
19
|
end
|
16
20
|
|
17
21
|
def has_role?(*role_names)
|
18
22
|
validate_role_names(role_names)
|
19
23
|
|
20
|
-
roles
|
24
|
+
(roles & role_names).any?
|
21
25
|
end
|
22
26
|
|
23
27
|
def assign_roles(*role_names, create_new: true)
|
@@ -25,25 +29,23 @@ module Rabarber
|
|
25
29
|
|
26
30
|
create_new_roles(role_names) if create_new
|
27
31
|
|
28
|
-
|
32
|
+
rabarber_roles << Role.where(name: role_names) - rabarber_roles
|
29
33
|
end
|
30
34
|
|
31
35
|
def revoke_roles(*role_names)
|
32
36
|
validate_role_names(role_names)
|
33
37
|
|
34
|
-
self.
|
38
|
+
self.rabarber_roles = rabarber_roles - Role.where(name: role_names)
|
35
39
|
end
|
36
40
|
|
37
41
|
private
|
38
42
|
|
39
43
|
def validate_role_names(role_names)
|
40
|
-
return if role_names.all?
|
41
|
-
[Symbol, String].include?(role_name.class) && role_name.match?(/\A[a-z0-9_]+\z/)
|
42
|
-
end
|
44
|
+
return if role_names.all? { |role_name| role_name.is_a?(Symbol) && role_name.to_s.match?(Role::NAME_REGEX) }
|
43
45
|
|
44
46
|
raise(
|
45
|
-
|
46
|
-
"Role names must be symbols
|
47
|
+
InvalidArgumentError,
|
48
|
+
"Role names must be symbols and may only contain lowercase letters, numbers and underscores"
|
47
49
|
)
|
48
50
|
end
|
49
51
|
|
data/lib/rabarber/models/role.rb
CHANGED
@@ -4,7 +4,9 @@ module Rabarber
|
|
4
4
|
class Role < ActiveRecord::Base
|
5
5
|
self.table_name = "rabarber_roles"
|
6
6
|
|
7
|
-
|
7
|
+
NAME_REGEX = /\A[a-z0-9_]+\z/
|
8
|
+
|
9
|
+
validates :name, presence: true, uniqueness: true, format: { with: NAME_REGEX }
|
8
10
|
|
9
11
|
def self.names
|
10
12
|
pluck(:name).map(&:to_sym)
|
data/lib/rabarber/rule.rb
CHANGED
@@ -5,9 +5,9 @@ module Rabarber
|
|
5
5
|
attr_reader :action, :roles, :custom
|
6
6
|
|
7
7
|
def initialize(action, roles, custom)
|
8
|
-
@action = action
|
9
|
-
@roles =
|
10
|
-
@custom = custom
|
8
|
+
@action = validate_action(action)
|
9
|
+
@roles = validate_roles(roles)
|
10
|
+
@custom = validate_custom_rule(custom)
|
11
11
|
end
|
12
12
|
|
13
13
|
def verify_access(user_roles, custom_rule_receiver, action_name = nil)
|
@@ -37,5 +37,30 @@ module Rabarber
|
|
37
37
|
custom_rule_receiver.send(custom)
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
def validate_action(action)
|
42
|
+
return action if action.nil? || action.is_a?(Symbol)
|
43
|
+
|
44
|
+
raise InvalidArgumentError, "Action name must be a symbol"
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_roles(roles)
|
48
|
+
roles_array = Array(roles)
|
49
|
+
|
50
|
+
return roles_array if roles_array.empty? || roles_array.all? do |role|
|
51
|
+
role.is_a?(Symbol) && role.match?(Role::NAME_REGEX)
|
52
|
+
end
|
53
|
+
|
54
|
+
raise(
|
55
|
+
InvalidArgumentError,
|
56
|
+
"Role names must be symbols and may only contain lowercase letters, numbers and underscores"
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_custom_rule(custom)
|
61
|
+
return custom if custom.nil? || custom.is_a?(Symbol) || custom.is_a?(Proc)
|
62
|
+
|
63
|
+
raise InvalidArgumentError, "Custom rule must be a symbol or a proc"
|
64
|
+
end
|
40
65
|
end
|
41
66
|
end
|
data/lib/rabarber/version.rb
CHANGED
data/lib/rabarber.rb
CHANGED
data/rabarber.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/rabarber/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "rabarber"
|
7
|
+
spec.version = Rabarber::VERSION
|
8
|
+
spec.authors = ["enjaku4"]
|
9
|
+
spec.email = ["enjaku4@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Simple authorization library for Ruby on Rails."
|
12
|
+
spec.homepage = "https://github.com/enjaku4/rabarber"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.required_ruby_version = ">= 3.0"
|
15
|
+
|
16
|
+
spec.files = [
|
17
|
+
"rabarber.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"
|
18
|
+
] + `git ls-files | grep -E '^(lib)'`.split("\n")
|
19
|
+
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_runtime_dependency "rails", ">= 6.1"
|
23
|
+
end
|
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: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- enjaku4
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -31,13 +31,9 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
-
- ".rspec"
|
35
|
-
- ".rubocop.yml"
|
36
34
|
- CHANGELOG.md
|
37
|
-
- Gemfile
|
38
35
|
- LICENSE.txt
|
39
36
|
- README.md
|
40
|
-
- Rakefile
|
41
37
|
- lib/generators/rabarber/roles_generator.rb
|
42
38
|
- lib/generators/rabarber/templates/create_rabarber_roles.rb.erb
|
43
39
|
- lib/rabarber.rb
|
@@ -50,6 +46,7 @@ files:
|
|
50
46
|
- lib/rabarber/permissions.rb
|
51
47
|
- lib/rabarber/rule.rb
|
52
48
|
- lib/rabarber/version.rb
|
49
|
+
- rabarber.gemspec
|
53
50
|
homepage: https://github.com/enjaku4/rabarber
|
54
51
|
licenses:
|
55
52
|
- MIT
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
require:
|
2
|
-
- rubocop-rails
|
3
|
-
- rubocop-rake
|
4
|
-
- rubocop-rspec
|
5
|
-
|
6
|
-
AllCops:
|
7
|
-
TargetRubyVersion: 3.0
|
8
|
-
|
9
|
-
Layout/LineLength:
|
10
|
-
Max: 120
|
11
|
-
|
12
|
-
Metrics/BlockLength:
|
13
|
-
Enabled: false
|
14
|
-
|
15
|
-
Naming/PredicateName:
|
16
|
-
Enabled: false
|
17
|
-
|
18
|
-
Rails/ApplicationController:
|
19
|
-
Exclude:
|
20
|
-
- spec/support/controllers.rb
|
21
|
-
|
22
|
-
Rails/ApplicationRecord:
|
23
|
-
Exclude:
|
24
|
-
- lib/rabarber/models/role.rb
|
25
|
-
- spec/support/models.rb
|
26
|
-
|
27
|
-
RSpec/ContextWording:
|
28
|
-
Enabled: false
|
29
|
-
|
30
|
-
RSpec/ExampleLength:
|
31
|
-
Enabled: false
|
32
|
-
|
33
|
-
RSpec/FilePath:
|
34
|
-
Enabled: false
|
35
|
-
|
36
|
-
RSpec/MultipleExpectations:
|
37
|
-
Enabled: false
|
38
|
-
|
39
|
-
RSpec/NamedSubject:
|
40
|
-
Enabled: false
|
41
|
-
|
42
|
-
RSpec/NestedGroups:
|
43
|
-
Enabled: false
|
44
|
-
|
45
|
-
Style/ClassVars:
|
46
|
-
Enabled: false
|
47
|
-
|
48
|
-
Style/Documentation:
|
49
|
-
Enabled: false
|
50
|
-
|
51
|
-
Style/Lambda:
|
52
|
-
Enabled: true
|
53
|
-
EnforcedStyle: literal
|
54
|
-
|
55
|
-
Style/StringLiterals:
|
56
|
-
Enabled: true
|
57
|
-
EnforcedStyle: double_quotes
|
58
|
-
|
59
|
-
Style/StringLiteralsInInterpolation:
|
60
|
-
Enabled: true
|
61
|
-
EnforcedStyle: double_quotes
|
62
|
-
|
63
|
-
Style/SymbolArray:
|
64
|
-
Enabled: true
|
65
|
-
EnforcedStyle: brackets
|
66
|
-
|
67
|
-
Style/WordArray:
|
68
|
-
Enabled: true
|
69
|
-
EnforcedStyle: brackets
|
data/Gemfile
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
gemspec
|
6
|
-
|
7
|
-
ENV["RAILS_VERSION"] ? gem("rails", ENV["RAILS_VERSION"]) : gem("rails", ">= 6.1")
|
8
|
-
|
9
|
-
gem "database_cleaner-active_record", "~> 2.1"
|
10
|
-
gem "rake", "~> 13.1"
|
11
|
-
gem "rspec", "~> 3.12"
|
12
|
-
gem "rspec-rails", "~> 6.1"
|
13
|
-
gem "rubocop", "~> 1.59"
|
14
|
-
gem "rubocop-rails", "~> 2.23"
|
15
|
-
gem "rubocop-rake", "~> 0.6"
|
16
|
-
gem "rubocop-rspec", "~> 2.25"
|
17
|
-
gem "sqlite3", "~> 1.7"
|