authority 2.2.0 → 2.3.0
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.
- data/CHANGELOG.markdown +6 -0
- data/Gemfile +1 -1
- data/README.markdown +27 -11
- data/TODO.markdown +5 -6
- data/lib/authority.rb +12 -21
- data/lib/authority/abilities.rb +3 -1
- data/lib/authority/controller.rb +31 -22
- data/lib/authority/security_violation.rb +15 -0
- data/lib/authority/user_abilities.rb +4 -0
- data/lib/authority/version.rb +1 -1
- data/spec/authority/abilities_spec.rb +76 -61
- data/spec/authority/authorizer_spec.rb +43 -39
- data/spec/authority/configuration_spec.rb +11 -11
- data/spec/authority/controller_spec.rb +151 -86
- data/spec/authority/integration_spec.rb +34 -29
- data/spec/authority/user_abilities_spec.rb +30 -20
- data/spec/authority_spec.rb +37 -41
- data/spec/spec_helper.rb +1 -0
- data/spec/support/{example_model.rb → example_classes.rb} +5 -1
- metadata +6 -7
- data/spec/support/example_controllers.rb +0 -22
- data/spec/support/user.rb +0 -3
data/CHANGELOG.markdown
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v2.3.0
|
4
|
+
|
5
|
+
- Added generic `current_user.can?(:mimic_lemurs)` for cases where there is no resource to work with. This calls a corresponding class method on `ApplicationAuthorizer`, like `ApplicationAuthorizer.can_mimic_lemurs?`.
|
6
|
+
- Renamed `authority_action` to `authority_actions` (plural) to reflect the fact that you can set multiple actions at once. Use of the old method will raise a deprecation warning.
|
7
|
+
- Lots of test cleanup so that test output is clearer - run rspec with `--format doc --order default` to see it.
|
8
|
+
|
3
9
|
## v2.2.0
|
4
10
|
|
5
11
|
Allow passing options hash to `authorize_action_for`, like `authorize_action_for(@llama, :sporting => @hat_style)`.
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -7,7 +7,8 @@ Authority will work fine with a standalone app or a single sign-on system. You c
|
|
7
7
|
It requires that you already have some kind of user object in your application, accessible from all controllers and views via a method like `current_user` (configurable).
|
8
8
|
|
9
9
|
[](http://travis-ci.org/nathanl/authority)
|
10
|
-
[](https://gemnasium.com/nathanl/authority)
|
10
|
+
<!-- [](https://gemnasium.com/nathanl/authority) -->
|
11
|
+
[](https://codeclimate.com/github/nathanl/authority)
|
11
12
|
|
12
13
|
## Contents
|
13
14
|
|
@@ -29,6 +30,7 @@ It requires that you already have some kind of user object in your application,
|
|
29
30
|
<li><a href="#controllers">Controllers</a></li>
|
30
31
|
<li><a href="#views">Views</a></li>
|
31
32
|
</ul></li>
|
33
|
+
<li><a href="#the_generic_can">The Generic `can?`</a>
|
32
34
|
<li><a href="#security_violations_and_logging">Security Violations & Logging</a></li>
|
33
35
|
<li><a href="#credits">Credits</a></li>
|
34
36
|
<li><a href="#contributing">Contributing</a></li>
|
@@ -45,8 +47,9 @@ Using Authority, you have:
|
|
45
47
|
- Fine-grained, **instance-level** rules. Examples:
|
46
48
|
- "Management users can only edit schedules with date ranges in the future."
|
47
49
|
- "Users can't create playlists more than 20 songs long unless they've paid."
|
48
|
-
- A clear syntax for permissions-based views.
|
50
|
+
- A clear syntax for permissions-based views. Examples:
|
49
51
|
- `link_to 'Edit Widget', edit_widget_path(@widget) if current_user.can_update?(@widget)`
|
52
|
+
- `link_to 'Keelhaul Scallywag', keelhaul_scallywag_path(@scallywag) if current_user.can_keelhaul?(@scallywag)`
|
50
53
|
- Graceful handling of access violations: by default, it displays a "you can't do that" screen and logs the violation.
|
51
54
|
- Minimal effort and mess.
|
52
55
|
|
@@ -265,12 +268,12 @@ describe AdminAuthorizer do
|
|
265
268
|
end
|
266
269
|
|
267
270
|
describe "class" do
|
268
|
-
it "
|
269
|
-
AdminAuthorizer.
|
271
|
+
it "lets admins update in bulk" do
|
272
|
+
expect(AdminAuthorizer).to be_bulk_updatable_by(@admin)
|
270
273
|
end
|
271
274
|
|
272
|
-
it "
|
273
|
-
AdminAuthorizer.
|
275
|
+
it "doesn't let users update in bulk" do
|
276
|
+
expect(AdminAuthorizer).not_to be_bulk_updatable_by(@user)
|
274
277
|
end
|
275
278
|
end
|
276
279
|
|
@@ -281,12 +284,12 @@ describe AdminAuthorizer do
|
|
281
284
|
@admin_resource_instance = mock_admin_resource
|
282
285
|
end
|
283
286
|
|
284
|
-
it "
|
285
|
-
@admin_resource_instance.authorizer.
|
287
|
+
it "lets admins delete" do
|
288
|
+
expect(@admin_resource_instance.authorizer).to be_deletable_by(@admin)
|
286
289
|
end
|
287
290
|
|
288
|
-
it "
|
289
|
-
@admin_resource_instance.authorizer.
|
291
|
+
it "doesn't let users delete" do
|
292
|
+
expect(@admin_resource_instance.authorizer).not_to be_deletable_by(@user)
|
290
293
|
end
|
291
294
|
|
292
295
|
end
|
@@ -326,7 +329,8 @@ class LlamasController < ApplicationController
|
|
326
329
|
authorize_actions_for Llama, :except => :create, :actions => {:neuter => :update},
|
327
330
|
|
328
331
|
# To authorize this controller's 'breed' action, ask whether `current_user.can_create?(Llama)`
|
329
|
-
|
332
|
+
# To authorize its 'vaporize' action, ask whether `current_user.can_delete?(Llama)`
|
333
|
+
authority_actions :breed => 'create', :vaporize => 'delete'
|
330
334
|
|
331
335
|
...
|
332
336
|
|
@@ -360,6 +364,17 @@ link_to 'Edit Widget', edit_widget_path(@widget) if current_user.can_update?(@wi
|
|
360
364
|
|
361
365
|
If the user isn't allowed to edit widgets, they won't see the link. If they're nosy and try to hit the URL directly, they'll get a [Security Violation](#security_violations_and_logging) from the controller.
|
362
366
|
|
367
|
+
<a name="the_generic_can">
|
368
|
+
## The Generic `can?`
|
369
|
+
|
370
|
+
Authority is organized around protecting resources. But **occasionally** you **may** need to authorize something that has no particular resource. For that, it provides the generic `can?` method. It works like this:
|
371
|
+
|
372
|
+
```ruby
|
373
|
+
current_user.can?(:view_stats_dashboard) # calls `ApplicationAuthorizer.can_view_stats_dashboard?`
|
374
|
+
```
|
375
|
+
|
376
|
+
Use this very sparingly, and consider it a [code smell](http://en.wikipedia.org/wiki/Code_smell). Overuse will turn your `ApplicationAuthorizer` into a junk drawer of methods. Ask yourself, "am I sure I don't have a resource for this? Should I have one?"
|
377
|
+
|
363
378
|
<a name="security_violations_and_logging">
|
364
379
|
## Security Violations & Logging
|
365
380
|
|
@@ -407,6 +422,7 @@ Your method will be handed the `SecurityViolation`, which has a `message` method
|
|
407
422
|
## Credits, AKA 'Shout-Outs'
|
408
423
|
|
409
424
|
- [adamhunter](https://github.com/adamhunter) for pairing with me on this gem. The only thing faster than his typing is his brain.
|
425
|
+
- [kevmoo](https://github.com/kevmoo), [MP211](https://github.com/MP211), and [scottmartin](https://github.com/scottmartin) for pitching in.
|
410
426
|
- [nkallen](https://github.com/nkallen) for writing [a lovely blog post on access control](http://pivotallabs.com/users/nick/blog/articles/272-access-control-permissions-in-rails) when he worked at Pivotal Labs. I cried sweet tears of joy when I read that a couple of years ago. I was like, "Zee access code, she is so BEEUTY-FUL!"
|
411
427
|
- [jnunemaker](https://github.com/jnunemaker) for later creating [Canable](http://github.com/jnunemaker/canable), another inspiration for Authority.
|
412
428
|
- [TMA](http://www.tma1.com) for employing me and letting me open source some of our code.
|
data/TODO.markdown
CHANGED
@@ -2,12 +2,11 @@
|
|
2
2
|
|
3
3
|
## Tests
|
4
4
|
|
5
|
-
-
|
5
|
+
- Test with Rails 4 and Ruby 2.0
|
6
6
|
- Test `ActionController` integration
|
7
|
+
- Add tests for the generators
|
7
8
|
|
8
|
-
##
|
9
|
-
|
10
|
-
### Use translation files
|
9
|
+
## Structural changes
|
11
10
|
|
12
|
-
-
|
13
|
-
-
|
11
|
+
- Consider the huge change from authorizer objects to modules for permissions. This eliminates the awkwardness of "to check a resource instance, let's go instantiate an authorizer and give it this resource instance..." If we make this change, describe a detailed upgrade path.
|
12
|
+
- Ensure that Authority can boot without the `configure` method having been run. Maybe this will mean having setters for `abilities` and `controller_action_map` that undefine and redefine those sets of methods if/when the user runs configuration.
|
data/lib/authority.rb
CHANGED
@@ -3,6 +3,7 @@ require 'active_support/core_ext/class/attribute'
|
|
3
3
|
require 'active_support/core_ext/hash/keys'
|
4
4
|
require 'active_support/core_ext/string/inflections'
|
5
5
|
require 'logger'
|
6
|
+
require 'authority/security_violation'
|
6
7
|
|
7
8
|
module Authority
|
8
9
|
|
@@ -30,16 +31,20 @@ module Authority
|
|
30
31
|
# @raise [SecurityViolation] if user is not allowed to perform action on resource
|
31
32
|
# @return [Model] resource instance
|
32
33
|
def self.enforce(action, resource, user, *options)
|
33
|
-
action_authorized
|
34
|
-
|
35
|
-
|
36
|
-
user.send("can_#{action}?", resource, Hash[*options])
|
37
|
-
end
|
38
|
-
raise SecurityViolation.new(user, action, resource) unless action_authorized
|
39
|
-
|
34
|
+
unless action_authorized?(action, resource, user, *options)
|
35
|
+
raise SecurityViolation.new(user, action, resource)
|
36
|
+
end
|
40
37
|
resource
|
41
38
|
end
|
42
39
|
|
40
|
+
def self.action_authorized?(action, resource, user, *options)
|
41
|
+
if options.empty?
|
42
|
+
user.send("can_#{action}?", resource)
|
43
|
+
else
|
44
|
+
user.send("can_#{action}?", resource, Hash[*options])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
43
48
|
class << self
|
44
49
|
attr_accessor :configuration
|
45
50
|
end
|
@@ -60,20 +65,6 @@ module Authority
|
|
60
65
|
require 'authority/user_abilities'
|
61
66
|
end
|
62
67
|
|
63
|
-
class SecurityViolation < StandardError
|
64
|
-
attr_reader :user, :action, :resource
|
65
|
-
|
66
|
-
def initialize(user, action, resource)
|
67
|
-
@user = user
|
68
|
-
@action = action
|
69
|
-
@resource = resource
|
70
|
-
end
|
71
|
-
|
72
|
-
def message
|
73
|
-
"#{@user} is not authorized to #{@action} this resource: #{@resource}"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
68
|
end
|
78
69
|
|
79
70
|
require 'authority/configuration'
|
data/lib/authority/abilities.rb
CHANGED
@@ -35,7 +35,9 @@ module Authority
|
|
35
35
|
def authorizer
|
36
36
|
@authorizer ||= authorizer_name.constantize # Get an actual reference to the authorizer class
|
37
37
|
rescue NameError
|
38
|
-
raise Authority::NoAuthorizerError.new(
|
38
|
+
raise Authority::NoAuthorizerError.new(
|
39
|
+
"#{authorizer_name} is set as the authorizer for #{self}, but the constant is missing"
|
40
|
+
)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
data/lib/authority/controller.rb
CHANGED
@@ -4,11 +4,6 @@ module Authority
|
|
4
4
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
|
-
included do
|
8
|
-
rescue_from(Authority::SecurityViolation, :with => Authority::Controller.security_violation_callback)
|
9
|
-
class_attribute :authority_resource
|
10
|
-
end
|
11
|
-
|
12
7
|
def self.security_violation_callback
|
13
8
|
Proc.new do |exception|
|
14
9
|
# Through the magic of ActiveSupport's `Proc#bind`, `ActionController::Base#rescue_from`
|
@@ -17,6 +12,11 @@ module Authority
|
|
17
12
|
end
|
18
13
|
end
|
19
14
|
|
15
|
+
included do
|
16
|
+
rescue_from(Authority::SecurityViolation, :with => Authority::Controller.security_violation_callback)
|
17
|
+
class_attribute :authority_resource
|
18
|
+
end
|
19
|
+
|
20
20
|
module ClassMethods
|
21
21
|
|
22
22
|
# Sets up before_filter to ensure user is allowed to perform a given controller action
|
@@ -26,17 +26,25 @@ module Authority
|
|
26
26
|
# ones and any other options applicable to a before_filter
|
27
27
|
def authorize_actions_for(model_class, options = {})
|
28
28
|
self.authority_resource = model_class
|
29
|
-
|
29
|
+
authority_actions(options[:actions] || {})
|
30
30
|
before_filter :run_authorization_check, options
|
31
31
|
end
|
32
32
|
|
33
33
|
# Allows defining and overriding a controller's map of its actions to the model's authorizer methods
|
34
34
|
#
|
35
35
|
# @param [Hash] action_map - controller actions and methods, to be merged with existing action_map
|
36
|
-
def
|
36
|
+
def authority_actions(action_map)
|
37
37
|
authority_action_map.merge!(action_map.symbolize_keys)
|
38
38
|
end
|
39
39
|
|
40
|
+
def authority_action(action_map)
|
41
|
+
puts "Authority's `authority_action` method has been renamed \
|
42
|
+
to `authority_actions` (plural) to reflect the fact that you can \
|
43
|
+
set multiple actions in one shot. Please update your controllers \
|
44
|
+
accordingly. (called from #{caller.first})".squeeze(' ')
|
45
|
+
authority_actions(action_map)
|
46
|
+
end
|
47
|
+
|
40
48
|
# The controller action to authority action map used for determining
|
41
49
|
# which Rails actions map to which authority actions (ex: index to read)
|
42
50
|
#
|
@@ -49,6 +57,22 @@ module Authority
|
|
49
57
|
|
50
58
|
protected
|
51
59
|
|
60
|
+
# To be run in a `before_filter`; ensure this controller action is allowed for the user
|
61
|
+
# Can be used directly within a controller action as well, given an instance or class with or
|
62
|
+
# without options to delegate to the authorizer.
|
63
|
+
#
|
64
|
+
# @param [Class] authority_resource, the model class associated with this controller
|
65
|
+
# @param [Hash] options, arbitrary options hash to forward up the chain to the authorizer
|
66
|
+
# @raise [MissingAction] if controller action isn't a key in `config.controller_action_map`
|
67
|
+
def authorize_action_for(authority_resource, *options)
|
68
|
+
# `action_name` comes from ActionController
|
69
|
+
authority_action = self.class.authority_action_map[action_name.to_sym]
|
70
|
+
if authority_action.nil?
|
71
|
+
raise MissingAction.new("No authority action defined for #{action_name}")
|
72
|
+
end
|
73
|
+
Authority.enforce(authority_action, authority_resource, authority_user, *options)
|
74
|
+
end
|
75
|
+
|
52
76
|
# Renders a static file to minimize the chances of further errors.
|
53
77
|
#
|
54
78
|
# @param [Exception] error, an error that indicates the user tried to perform a forbidden action.
|
@@ -73,21 +97,6 @@ module Authority
|
|
73
97
|
send(Authority.configuration.user_method)
|
74
98
|
end
|
75
99
|
|
76
|
-
# To be run in a `before_filter`; ensure this controller action is allowed for the user
|
77
|
-
# Can be used directly within a controller action as well, given an instance or class with or
|
78
|
-
# without options to delegate to the authorizer.
|
79
|
-
#
|
80
|
-
# @param [Class] authority_resource, the model class associated with this controller
|
81
|
-
# @param [Hash] options, arbitrary options hash to forward up the chain to the authorizer
|
82
|
-
# @raise [MissingAction] if controller action isn't a key in `config.controller_action_map`
|
83
|
-
def authorize_action_for(authority_resource, *options)
|
84
|
-
authority_action = self.class.authority_action_map[action_name.to_sym]
|
85
|
-
if authority_action.nil?
|
86
|
-
raise MissingAction.new("No authority action defined for #{action_name}")
|
87
|
-
end
|
88
|
-
Authority.enforce(authority_action, authority_resource, authority_user, *options)
|
89
|
-
end
|
90
|
-
|
91
100
|
class MissingAction < StandardError ; end
|
92
101
|
end
|
93
102
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Authority
|
2
|
+
class SecurityViolation < StandardError
|
3
|
+
attr_reader :user, :action, :resource
|
4
|
+
|
5
|
+
def initialize(user, action, resource)
|
6
|
+
@user = user
|
7
|
+
@action = action
|
8
|
+
@resource = resource
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
"#{@user} is not authorized to #{@action} this resource: #{@resource}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/authority/version.rb
CHANGED
@@ -1,44 +1,50 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'support/
|
3
|
-
require 'support/user'
|
2
|
+
require 'support/example_classes'
|
4
3
|
|
5
4
|
describe Authority::Abilities do
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
end
|
6
|
+
let(:user) { ExampleUser.new }
|
7
|
+
let(:resource_class) { ExampleResource }
|
10
8
|
|
11
|
-
describe "
|
9
|
+
describe "instance methods" do
|
12
10
|
|
13
|
-
|
14
|
-
ExampleModel.should respond_to(:authorizer_name)
|
15
|
-
end
|
11
|
+
describe "authorizer_name" do
|
16
12
|
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
it "has a class attribute getter for authorizer_name" do
|
14
|
+
expect(resource_class).to respond_to(:authorizer_name)
|
15
|
+
end
|
20
16
|
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
it "has a class attribute setter for authorizer_name" do
|
18
|
+
expect(resource_class).to respond_to(:authorizer_name=)
|
19
|
+
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
ExampleModel.authorizer
|
29
|
-
end
|
21
|
+
it "has a default authorizer_name of 'ApplicationAuthorizer'" do
|
22
|
+
expect(resource_class.authorizer_name).to eq("ApplicationAuthorizer")
|
23
|
+
end
|
30
24
|
|
31
|
-
it "should memoize the authorizer to avoid reconstantizing" do
|
32
|
-
ExampleModel.authorizer
|
33
|
-
ExampleModel.authorizer_name.should_not_receive(:constantize)
|
34
|
-
ExampleModel.authorizer
|
35
25
|
end
|
36
26
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
27
|
+
describe "authorizer" do
|
28
|
+
|
29
|
+
it "constantizes the authorizer name as the authorizer" do
|
30
|
+
resource_class.instance_variable_set(:@authorizer, nil)
|
31
|
+
resource_class.authorizer_name.should_receive(:constantize)
|
32
|
+
resource_class.authorizer
|
33
|
+
end
|
34
|
+
|
35
|
+
it "memoizes the authorizer to avoid reconstantizing" do
|
36
|
+
resource_class.authorizer
|
37
|
+
resource_class.authorizer_name.should_not_receive(:constantize)
|
38
|
+
resource_class.authorizer
|
39
|
+
end
|
40
|
+
|
41
|
+
it "raises a friendly error if the authorizer doesn't exist" do
|
42
|
+
class NoAuthorizerModel < resource_class; end ;
|
43
|
+
NoAuthorizerModel.instance_variable_set(:@authorizer, nil)
|
44
|
+
NoAuthorizerModel.authorizer_name = 'NonExistentAuthorizer'
|
45
|
+
expect { NoAuthorizerModel.authorizer }.to raise_error(Authority::NoAuthorizerError)
|
46
|
+
end
|
47
|
+
|
42
48
|
end
|
43
49
|
|
44
50
|
end
|
@@ -48,24 +54,28 @@ describe Authority::Abilities do
|
|
48
54
|
Authority.adjectives.each do |adjective|
|
49
55
|
method_name = "#{adjective}_by?"
|
50
56
|
|
51
|
-
it "
|
52
|
-
|
57
|
+
it "responds to `#{method_name}`" do
|
58
|
+
expect(resource_class).to respond_to(method_name)
|
53
59
|
end
|
54
60
|
|
55
|
-
describe "
|
61
|
+
describe "#{method_name}" do
|
62
|
+
|
63
|
+
context "when given an options hash" do
|
64
|
+
|
65
|
+
it "delegates `#{method_name}` to its authorizer class, passing the options" do
|
66
|
+
resource_class.authorizer.should_receive(method_name).with(user, :lacking => 'nothing')
|
67
|
+
resource_class.send(method_name, user, :lacking => 'nothing')
|
68
|
+
end
|
56
69
|
|
57
|
-
it "should delegate `#{method_name}` to its authorizer class, passing the options" do
|
58
|
-
ExampleModel.authorizer.should_receive(method_name).with(@user, :lacking => 'nothing')
|
59
|
-
ExampleModel.send(method_name, @user, :lacking => 'nothing')
|
60
70
|
end
|
61
71
|
|
62
|
-
|
72
|
+
context "when not given an options hash" do
|
63
73
|
|
64
|
-
|
74
|
+
it "delegates `#{method_name}` to its authorizer class, passing no options" do
|
75
|
+
resource_class.authorizer.should_receive(method_name).with(user)
|
76
|
+
resource_class.send(method_name, user)
|
77
|
+
end
|
65
78
|
|
66
|
-
it "should delegate `#{method_name}` to its authorizer class, passing no options" do
|
67
|
-
ExampleModel.authorizer.should_receive(method_name).with(@user)
|
68
|
-
ExampleModel.send(method_name, @user)
|
69
79
|
end
|
70
80
|
|
71
81
|
end
|
@@ -76,50 +86,55 @@ describe Authority::Abilities do
|
|
76
86
|
|
77
87
|
describe "instance methods" do
|
78
88
|
|
89
|
+
let(:resource_instance) { resource_class.new }
|
90
|
+
|
79
91
|
before :each do
|
80
|
-
@
|
81
|
-
@authorizer = ExampleModel.authorizer.new(@example_model)
|
92
|
+
@authorizer = resource_class.authorizer.new(resource_instance)
|
82
93
|
end
|
83
94
|
|
84
95
|
Authority.adjectives.each do |adjective|
|
85
96
|
method_name = "#{adjective}_by?"
|
86
97
|
|
87
|
-
it "
|
88
|
-
|
98
|
+
it "responds to `#{method_name}`" do
|
99
|
+
expect(resource_instance).to respond_to(method_name)
|
89
100
|
end
|
90
101
|
|
91
|
-
describe "
|
102
|
+
describe "#{method_name}" do
|
103
|
+
|
104
|
+
context "when given an options hash" do
|
105
|
+
|
106
|
+
it "delegates `#{method_name}` to a new authorizer instance, passing the options" do
|
107
|
+
resource_class.authorizer.stub(:new).and_return(@authorizer)
|
108
|
+
@authorizer.should_receive(method_name).with(user, :with => 'mayo')
|
109
|
+
resource_instance.send(method_name, user, :with => 'mayo')
|
110
|
+
end
|
92
111
|
|
93
|
-
it "should delegate `#{method_name}` to a new authorizer instance, passing the options" do
|
94
|
-
ExampleModel.authorizer.stub(:new).and_return(@authorizer)
|
95
|
-
@authorizer.should_receive(method_name).with(@user, :with => 'mayo')
|
96
|
-
@example_model.send(method_name, @user, :with => 'mayo')
|
97
112
|
end
|
98
113
|
|
99
|
-
|
114
|
+
context "when not given an options hash" do
|
115
|
+
|
116
|
+
it "delegates `#{method_name}` to a new authorizer instance, passing no options" do
|
117
|
+
resource_class.authorizer.stub(:new).and_return(@authorizer)
|
118
|
+
@authorizer.should_receive(method_name).with(user)
|
119
|
+
resource_instance.send(method_name, user)
|
120
|
+
end
|
100
121
|
|
101
|
-
describe "if not given an options hash" do
|
102
|
-
|
103
|
-
it "should delegate `#{method_name}` to a new authorizer instance, passing no options" do
|
104
|
-
ExampleModel.authorizer.stub(:new).and_return(@authorizer)
|
105
|
-
@authorizer.should_receive(method_name).with(@user)
|
106
|
-
@example_model.send(method_name, @user)
|
107
122
|
end
|
108
123
|
|
109
124
|
end
|
110
125
|
|
111
126
|
end
|
112
127
|
|
113
|
-
it "
|
114
|
-
|
128
|
+
it "provides an accessor for its authorizer" do
|
129
|
+
expect(resource_instance).to respond_to(:authorizer)
|
115
130
|
end
|
116
131
|
|
117
132
|
# When checking instance methods, we want to ensure that every check uses a new
|
118
133
|
# instance of the authorizer. Otherwise, you might check, make a change to the
|
119
134
|
# model instance, check again, and get an outdated answer.
|
120
|
-
it "
|
121
|
-
|
122
|
-
2.times {
|
135
|
+
it "always creates a new authorizer instance when accessing the authorizer" do
|
136
|
+
resource_instance.class.authorizer.should_receive(:new).with(resource_instance).twice
|
137
|
+
2.times { resource_instance.authorizer }
|
123
138
|
end
|
124
139
|
|
125
140
|
end
|