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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f7d080588bb06feadb9a4f9759415e24828907c520c95bd4174a242820774cf
4
- data.tar.gz: 40a1c865a5fdcfd84980718d61c74cc23ab342a00e1bb933a82ed13b6fbc2a9c
3
+ metadata.gz: f83c97e518621ea7749eab381d5e1e4f84785c8197036e923220fa0d29fcc43e
4
+ data.tar.gz: 9fe75afecf6c3066c0cbf779ffdf79a2e67716a8acdc0f15c72e85008528f5ab
5
5
  SHA512:
6
- metadata.gz: 0a4f3c2b737c6ad4dff2179aec47835f206b6b750b59a7a3210ba83a905840da679003dad71a9d2b62e07227ad57d861373178cacc2b5ff61431bbbf11aa7023
7
- data.tar.gz: d8a6723c767a24b7653c29b46a2df9ea95a9f2c8994c89e9f2be56d0e00e5024b2b8462aa89a754786322ebf780477fae0db791891e2423ea8d5a28a50866b0a
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 improvements for gem development
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
- #### Example of Usage:
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
- ## Usage
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
- ### This adds the following methods:
92
+ This adds the following methods:
93
93
 
94
- #### `#assign_roles`
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
- #### `#revoke_roles`
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
- #### `#has_role?`
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
- #### `#roles`
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
- Utilize these 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.
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
- ### Authorization Rules
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 to the controller and its children. This method allows you to define the authorization rules.
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
- unless [Symbol, String].include?(method_name.class)
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 ArgumentError, "Value must be a boolean" unless [true, false].include?(value)
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 ArgumentError, "Proc is expected" unless callable.is_a?(Proc)
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.names, self.class, action_name.to_sym, self
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 :roles, class_name: "Rabarber::Role",
13
- foreign_key: "roleable_id",
14
- join_table: "rabarber_roles_roleables"
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.exists?(name: role_names)
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
- roles << Role.where(name: role_names) - roles
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.roles = roles - Role.where(name: role_names)
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? do |role_name|
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
- ArgumentError,
46
- "Role names must be symbols or strings and may only contain lowercase letters and underscores"
47
+ InvalidArgumentError,
48
+ "Role names must be symbols and may only contain lowercase letters, numbers and underscores"
47
49
  )
48
50
  end
49
51
 
@@ -4,7 +4,9 @@ module Rabarber
4
4
  class Role < ActiveRecord::Base
5
5
  self.table_name = "rabarber_roles"
6
6
 
7
- validates :name, presence: true, uniqueness: true
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 = Array(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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.3"
5
5
  end
data/lib/rabarber.rb CHANGED
@@ -23,4 +23,6 @@ module Rabarber
23
23
  end
24
24
 
25
25
  class Error < StandardError; end
26
+ class ConfigurationError < Error; end
27
+ class InvalidArgumentError < Error; end
26
28
  end
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.1
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: 2023-12-31 00:00:00.000000000 Z
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
@@ -1,4 +0,0 @@
1
- --format progress
2
- --color
3
- --require spec_helper
4
- --order rand
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"
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rubocop/rake_task"
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: [:spec, :rubocop]