authority 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://secure.travis-ci.org/nathanl/authority.png?branch=master)](http://travis-ci.org/nathanl/authority)
|
10
|
-
[![Dependency Status](https://gemnasium.com/nathanl/authority.png)](https://gemnasium.com/nathanl/authority)
|
10
|
+
<!-- [![Dependency Status](https://gemnasium.com/nathanl/authority.png)](https://gemnasium.com/nathanl/authority) -->
|
11
|
+
[![Code Climate](https://codeclimate.com/badge.png)](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
|