rails_authorize 1.0.0 → 1.5.0

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: e8f0d5120896348f98b7f268593565d38a58b12478fdb9b622f429fcf76eaa28
4
- data.tar.gz: 163ad731aaeaaaf3d766abc3de284f65a12e1d507e2bf67a02be8a090d25cd6e
3
+ metadata.gz: a84a4e7ebae1410009ffac8f0a7728eb6644f4eaa90b07724a59f469edc746ae
4
+ data.tar.gz: 9d28518dd6f5b0e3f647ba7977c6ffddd87d2a83f39255cc166b2c88bb1d7c92
5
5
  SHA512:
6
- metadata.gz: f2ca97240e596d0a505edda8fe402f27cc2161648a166127b84e394eb111d2562b434fb0c71b08af64b2d541071b5a8017dcfcb7a22b34fcf91599f044e74790
7
- data.tar.gz: 3ff394b863f95512dfd0ef6b9889ab8b5153ce452bff9a8c73f32573a9deca451004dda547f8c5f8255976b051e37049b958b9679749572d257d7687b9ba146d
6
+ metadata.gz: b2df0ac9bb1dd40b902062b1557acf9ead2881a55d4580e289364ec7e69bf5a2ed8b9046e256d59bc77d9332e16d8d91fd491886bd4c9551941193bed0df6f0b
7
+ data.tar.gz: 5022ee3cf9f35e3d4f8b9b9081312ec5cdff05b9f16d6afe1a119f02e72e71ba9165d8f4a193456c2d08a0805d24dcaa43177ab3f1230fc3cf7626097ab61f0a
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ *.gem
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
@@ -1,5 +1,5 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.3.4
4
+ - 2.5.5
5
5
  before_install: gem install bundler -v 1.15.4
data/Gemfile CHANGED
@@ -4,3 +4,8 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in rails_authorize.gemspec
6
6
  gemspec
7
+
8
+ group :test do
9
+ gem 'activesupport', '>= 5.0.0'
10
+ gem 'actionpack', '>= 5.0.0'
11
+ end
@@ -1,48 +1,81 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rails_authorize (1.0.0)
5
- activesupport (>= 3.0.0)
4
+ rails_authorize (1.5.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
9
8
  specs:
10
- activesupport (5.2.0)
9
+ actionpack (6.0.3.2)
10
+ actionview (= 6.0.3.2)
11
+ activesupport (= 6.0.3.2)
12
+ rack (~> 2.0, >= 2.0.8)
13
+ rack-test (>= 0.6.3)
14
+ rails-dom-testing (~> 2.0)
15
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
16
+ actionview (6.0.3.2)
17
+ activesupport (= 6.0.3.2)
18
+ builder (~> 3.1)
19
+ erubi (~> 1.4)
20
+ rails-dom-testing (~> 2.0)
21
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
22
+ activesupport (6.0.3.2)
11
23
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
24
  i18n (>= 0.7, < 2)
13
25
  minitest (~> 5.1)
14
26
  tzinfo (~> 1.1)
15
- concurrent-ruby (1.0.5)
16
- diff-lcs (1.3)
17
- i18n (1.0.0)
27
+ zeitwerk (~> 2.2, >= 2.2.2)
28
+ builder (3.2.4)
29
+ concurrent-ruby (1.1.6)
30
+ crass (1.0.6)
31
+ diff-lcs (1.4.4)
32
+ erubi (1.9.0)
33
+ i18n (1.8.3)
18
34
  concurrent-ruby (~> 1.0)
19
- minitest (5.11.3)
20
- rake (10.5.0)
21
- rspec (3.7.0)
22
- rspec-core (~> 3.7.0)
23
- rspec-expectations (~> 3.7.0)
24
- rspec-mocks (~> 3.7.0)
25
- rspec-core (3.7.1)
26
- rspec-support (~> 3.7.0)
27
- rspec-expectations (3.7.0)
35
+ loofah (2.6.0)
36
+ crass (~> 1.0.2)
37
+ nokogiri (>= 1.5.9)
38
+ mini_portile2 (2.4.0)
39
+ minitest (5.14.1)
40
+ nokogiri (1.10.10)
41
+ mini_portile2 (~> 2.4.0)
42
+ rack (2.2.3)
43
+ rack-test (1.1.0)
44
+ rack (>= 1.0, < 3)
45
+ rails-dom-testing (2.0.3)
46
+ activesupport (>= 4.2.0)
47
+ nokogiri (>= 1.6)
48
+ rails-html-sanitizer (1.3.0)
49
+ loofah (~> 2.3)
50
+ rake (13.0.1)
51
+ rspec (3.9.0)
52
+ rspec-core (~> 3.9.0)
53
+ rspec-expectations (~> 3.9.0)
54
+ rspec-mocks (~> 3.9.0)
55
+ rspec-core (3.9.2)
56
+ rspec-support (~> 3.9.3)
57
+ rspec-expectations (3.9.2)
28
58
  diff-lcs (>= 1.2.0, < 2.0)
29
- rspec-support (~> 3.7.0)
30
- rspec-mocks (3.7.0)
59
+ rspec-support (~> 3.9.0)
60
+ rspec-mocks (3.9.1)
31
61
  diff-lcs (>= 1.2.0, < 2.0)
32
- rspec-support (~> 3.7.0)
33
- rspec-support (3.7.1)
62
+ rspec-support (~> 3.9.0)
63
+ rspec-support (3.9.3)
34
64
  thread_safe (0.3.6)
35
- tzinfo (1.2.5)
65
+ tzinfo (1.2.7)
36
66
  thread_safe (~> 0.1)
67
+ zeitwerk (2.4.0)
37
68
 
38
69
  PLATFORMS
39
70
  ruby
40
71
 
41
72
  DEPENDENCIES
42
- bundler (~> 1.15)
73
+ actionpack (>= 5.0.0)
74
+ activesupport (>= 5.0.0)
75
+ bundler (~> 2.1)
43
76
  rails_authorize!
44
- rake (~> 10)
77
+ rake (~> 13)
45
78
  rspec (~> 3.0)
46
79
 
47
80
  BUNDLED WITH
48
- 1.16.1
81
+ 2.1.4
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # RailsAuthorize
2
+ [![Gem Version](https://badge.fury.io/rb/rails_authorize.svg)](https://badge.fury.io/rb/rails_authorize)
2
3
  ![Build Status](https://travis-ci.org/rjurado01/rails_authorize.svg?branch=master)
3
4
 
4
5
  Simple and flexible authorization Rails system inspired by Pundit.
@@ -60,6 +61,14 @@ class PostPolicy < ApplicationPolicy
60
61
  def scope
61
62
  target.where(published: true)
62
63
  end
64
+
65
+ def permitted_attributes
66
+ if user.admin?
67
+ %i[status body title]
68
+ else
69
+ %i[body title]
70
+ end
71
+ end
63
72
  end
64
73
  ```
65
74
 
@@ -77,15 +86,235 @@ end
77
86
  class PostController
78
87
  def index
79
88
  @posts = authorized_scope(Post)
89
+ ...
90
+ end
91
+
92
+ def update
93
+ @post = Post.find(params[:id])
94
+ @post.update(permitted_attributes(@post))
95
+ ...
80
96
  end
81
97
 
82
98
  def show
83
99
  @post = Post.find(params[:id])
84
100
  authorize @post
101
+ ...
102
+ end
103
+ end
104
+ ```
105
+
106
+ ## Customize user
107
+
108
+ Rails Authorize will call the `current_user` method to retrieve the user for authorization. If you need to customize it you can pass `user` as option to method `authorize`:
109
+
110
+ ```ruby
111
+ # app/controllers/posts_controller.rb
112
+
113
+ class PostController
114
+ def show
115
+ @post = Post.find(params[:id])
116
+ @user = User.find(params[:user_id])
117
+ authorize @post, user: @user
118
+ ...
119
+ end
120
+ end
121
+ ```
122
+
123
+ ## Customize action
124
+
125
+ Rails Authorize will use the controller action name as identifier of policy method to use for authorization. If you need to customize it you can pass `action` as option to method `authorize`:
126
+
127
+ ```ruby
128
+ # app/controllers/posts_controller.rb
129
+
130
+ class PostController
131
+ def show
132
+ @post = Post.find(params[:id])
133
+ authorize @post, action: :custom_action?
134
+ ...
135
+ end
136
+ end
137
+ ```
138
+
139
+ ## Define context
140
+
141
+ Rails Authorize allow you to define the context objects that you need to authorize an action:
142
+
143
+ ```ruby
144
+ # app/controllers/posts_controller.rb
145
+
146
+ class PostController
147
+ def show
148
+ @post = Post.find(params[:id])
149
+ authorize @post, context: {template: params[:template]}
150
+ ...
151
+ end
152
+ end
153
+ ```
154
+
155
+ ```ruby
156
+ # app/policies/post_policy.rb
157
+
158
+ class PostPolicy < ApplicationPolicy
159
+ def show?
160
+ if context[:template] == 'complete' ?
161
+ user.is_admin?
162
+ else
163
+ true
164
+ end
165
+ end
166
+ end
167
+ ```
168
+
169
+ ## Strong parameters
170
+
171
+ Rails uses [strong_parameters](http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters) to handle mass-assignment protection in the controller. With this gem you can control which attributes a user has access via your policies.
172
+
173
+ You can set up a `permitted_attributes` method in your policy like this:
174
+
175
+ ```ruby
176
+ # app/policies/post_policy.rb
177
+
178
+ class PostPolicy < ApplicationPolicy
179
+ def permitted_attributes
180
+ if user.admin?
181
+ %i[status body title]
182
+ else
183
+ %i[body title]
184
+ end
85
185
  end
86
186
  end
87
187
  ```
88
188
 
189
+ You can now retrieve these attributes from the policy:
190
+
191
+ ```ruby
192
+ policy(@post).permitted_attributes
193
+ ```
194
+
195
+ Rails Authorize provides `permitted_attributes` helper method to use it in your controllers:
196
+
197
+ ```ruby
198
+ # app/controllers/posts_controller.rb
199
+
200
+ class PostController
201
+ def update
202
+ @post.update(permitted_attributes(@post))
203
+ end
204
+ end
205
+ ```
206
+
207
+ By default `permitted_attributes` makes `params.require(:post)` if you want to personalize what attribute is required in params, your policy must define a `param_key`:
208
+
209
+ ```ruby
210
+ # app/policies/post_policy.rb
211
+
212
+ class PostPolicy < ApplicationPolicy
213
+ def param_key
214
+ 'custom_key'
215
+ end
216
+ end
217
+ ```
218
+
219
+ Also, you can pass custom key as option using `param_key` for specific situations:
220
+
221
+ ```ruby
222
+ # app/controllers/posts_controller.rb
223
+
224
+ class PostController
225
+ def update
226
+ @post.update(permitted_attributes(@post, param_key: 'custom_key'))
227
+ end
228
+ end
229
+ ```
230
+
231
+ If you want to permit different attributes based on the current action, you can define a `permitted_attributes_for_#{action_name}` method on your policy:
232
+
233
+ ```ruby
234
+ # app/policies/post_policy.rb
235
+
236
+ class PostPolicy < ApplicationPolicy
237
+ def permitted_attributes_for_create
238
+ [:title, :body]
239
+ end
240
+
241
+ def permitted_attributes_for_update
242
+ [:body]
243
+ end
244
+ end
245
+ ```
246
+
247
+ ## Use without target
248
+
249
+ Sometimes you need to authorize a controller action that it doesn't use a model to authorize.
250
+
251
+ For this situations you can omit `target` and pass only options with `policy` to `authorize` or `permitted_attributes`:
252
+
253
+ ```ruby
254
+ # app/controllers/custom_controller.rb
255
+
256
+ class CustomController
257
+ def show
258
+ authorize policy: CustomPolicy
259
+ ...
260
+ end
261
+
262
+ def create
263
+ resource = Resource.new(permitted_attributes(policy: CustomPolicy))
264
+ ...
265
+ end
266
+ end
267
+ ```
268
+
269
+ ```ruby
270
+ # app/policies/custom_policy.rb
271
+
272
+ class CustomPolicy < ApplicationPolicy
273
+ def show?
274
+ # target is nil
275
+ ...
276
+ end
277
+
278
+ def permitted_attributes
279
+ [:title, :body]
280
+ end
281
+ end
282
+ ```
283
+
284
+
285
+
286
+ ## Ensuring authorization and scoping are performed
287
+
288
+ In certain kind of applications where almost all or even the whole application is private, in each of the actions you have to make sure that authorization is performed. To make sure that developers perform authorization, RailsAuthorize provides two methods. `verify_authorized` makes sure that authorization is performed, and likewise `verify_policy_scoped` checks that scoping is performed
289
+
290
+ Both methods are mainly aimed to be called on `after_action`.
291
+ ```ruby
292
+ class ApplicationController < ActionController::Base
293
+ include RailsAuthorize
294
+ after_action :verify_authorized, except: :index
295
+ after_action :verify_policy_scoped, only: :index
296
+ end
297
+ ```
298
+
299
+ ### Skipping verification
300
+
301
+ If you're using `verify_authorized` in your controllers but need to conditionally bypass verification, you can use `skip_authorization`. For bypassing `verify_policy_scoped`, use `skip_policy_scope`.
302
+ ```ruby
303
+ def create
304
+ record = Record.new(attributes)
305
+
306
+ if record.valid?
307
+ authorize record
308
+ else
309
+ skip_authorization
310
+ end
311
+ end
312
+ ```
313
+
314
+ ## Rspec
315
+
316
+ For writing expressive tests for your policies in RSpec you can use this gem: [rails_authorize_matchers](https://github.com/pacop/rails_authorize_matchers)
317
+
89
318
  ## Development
90
319
 
91
320
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -3,6 +3,8 @@ require 'rails_authorize/version'
3
3
  module RailsAuthorize
4
4
  # Error that will be raised when authorization has failed
5
5
  class NotAuthorizedError < StandardError; end
6
+ class AuthorizationNotPerformedError < StandardError; end
7
+ class ScopingNotPerformedError < StandardError; end
6
8
 
7
9
  ##
8
10
  # Finds policy class for given target and returns new instance
@@ -19,7 +21,7 @@ module RailsAuthorize
19
21
  user = options[:user] || current_user
20
22
  klass = options[:policy] || "#{target.model_name.name}Policy".constantize
21
23
 
22
- klass.new(user, target, options[:context])
24
+ klass.new(user, target, options[:context] || {})
23
25
  end
24
26
 
25
27
  ##
@@ -33,12 +35,16 @@ module RailsAuthorize
33
35
  # @return [Object] the passed target
34
36
  #
35
37
  def authorize(target, options={})
38
+ return authorize(nil, target) if target.is_a?(Hash)
39
+
36
40
  action = options.delete(:action) || "#{action_name}?"
37
41
  policy = policy(target, options)
38
42
 
39
43
  raise(NotAuthorizedError) unless policy.public_send(action)
40
44
 
41
- target
45
+ @_policy_authorized = true
46
+
47
+ target || true
42
48
  end
43
49
 
44
50
  ##
@@ -50,6 +56,8 @@ module RailsAuthorize
50
56
  # @return [Scope] policy scope
51
57
  #
52
58
  def policy_scope(target, options={})
59
+ @_policy_scoped = true
60
+
53
61
  policy(target, options).scope
54
62
  end
55
63
 
@@ -69,6 +77,85 @@ module RailsAuthorize
69
77
 
70
78
  raise(NotAuthorizedError) unless policy.public_send(action)
71
79
 
80
+ @_policy_scoped = @_policy_authorized = true
81
+
72
82
  policy.scope
73
83
  end
84
+
85
+ # Retrieves a set of permitted attributes from the policy by instantiating
86
+ # the policy class for the given record and calling `permitted_attributes` on
87
+ # it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
88
+ # what key the record should have in the params hash and retrieves the
89
+ # permitted attributes from the params hash under that key.
90
+ #
91
+ # @param record [Object] the object we're retrieving permitted attributes for
92
+ # @param options [Hash] key/value options (action, user, policy, context)
93
+ # @param options[:action] [String] the method to check on the policy (e.g. `:show?`)
94
+ # @return [Hash{String => Object}] the permitted attributes
95
+ def permitted_attributes(target, options={})
96
+ return permitted_attributes(nil, target) if target.is_a?(Hash)
97
+
98
+ action = options.delete(:action) || action_name
99
+ policy = policy(target, options)
100
+
101
+ method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
102
+ "permitted_attributes_for_#{action}"
103
+ else
104
+ 'permitted_attributes'
105
+ end
106
+
107
+ param_key = if options[:param_key]
108
+ options[:param_key]
109
+ elsif policy.try(:param_key).present?
110
+ policy.param_key
111
+ else
112
+ target.model_name.name.underscore
113
+ end
114
+
115
+ params.require(param_key).permit(*policy.public_send(method_name))
116
+ end
117
+
118
+ # Raises an error if authorization has not been performed
119
+ #
120
+ # @raise [AuthorizationNotPerformedError] if authorization has not been performed
121
+ # @return [void]
122
+ def verify_authorized
123
+ raise AuthorizationNotPerformedError, self.class unless policy_authorized?
124
+ end
125
+
126
+ # Allow this action not to perform authorization.
127
+ #
128
+ # @return [void]
129
+ def skip_authorization
130
+ @_policy_authorized = true
131
+ end
132
+
133
+ # Raises an error if policy scoping has not been performed
134
+ #
135
+ # @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
136
+ # @return [void]
137
+ def verify_policy_scoped
138
+ raise ScopingNotPerformedError, self.class unless policy_scoped?
139
+ end
140
+
141
+ # Allow this action not to perform policy scoping.
142
+ #
143
+ # @return [void]
144
+ def skip_policy_scope
145
+ @_policy_scoped = true
146
+ end
147
+
148
+ protected
149
+
150
+ # @return [Boolean] whether authorization has been performed, i.e. whether
151
+ # one {#authorize} or {#skip_authorization} has been called
152
+ def policy_authorized?
153
+ @_policy_authorized == true
154
+ end
155
+
156
+ # @return [Boolean] whether policy scoping has been performed, i.e. whether
157
+ # one {#policy_scope} or {#skip_policy_scope} has been called
158
+ def policy_scoped?
159
+ @_policy_scoped == true
160
+ end
74
161
  end
@@ -1,3 +1,3 @@
1
1
  module RailsAuthorize
2
- VERSION = "1.0.0"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -20,9 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_development_dependency 'bundler', '~> 1.15'
24
- spec.add_development_dependency 'rake', '~> 10'
23
+ spec.add_development_dependency 'bundler', '~> 2.1'
24
+ spec.add_development_dependency 'rake', '~> 13'
25
25
  spec.add_development_dependency 'rspec', '~> 3.0'
26
-
27
- spec.add_dependency 'activesupport', '>= 3.0.0'
28
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_authorize
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rjurado01
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-11 00:00:00.000000000 Z
11
+ date: 2020-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.15'
19
+ version: '2.1'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.15'
26
+ version: '2.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10'
33
+ version: '13'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10'
40
+ version: '13'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
- - !ruby/object:Gem::Dependency
56
- name: activesupport
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 3.0.0
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: 3.0.0
69
55
  description: Authorization system for Rails with only few helpers and regular Ruby
70
56
  classes.
71
57
  email:
@@ -106,8 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
92
  - !ruby/object:Gem::Version
107
93
  version: '0'
108
94
  requirements: []
109
- rubyforge_project:
110
- rubygems_version: 2.7.3
95
+ rubygems_version: 3.1.2
111
96
  signing_key:
112
97
  specification_version: 4
113
98
  summary: Simple and flexible authorization Rails system