authority 3.0.0 → 3.1.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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -3
- data/CHANGELOG.markdown +10 -0
- data/Gemfile +0 -2
- data/README.markdown +13 -7
- data/authority.gemspec +2 -0
- data/gemfiles/3.2.gemfile +4 -4
- data/gemfiles/4.0.gemfile +4 -4
- data/gemfiles/4.1.gemfile +4 -4
- data/gemfiles/4.2.gemfile +6 -0
- data/lib/authority.rb +1 -1
- data/lib/authority/authorizer.rb +14 -7
- data/lib/authority/controller.rb +12 -3
- data/lib/authority/version.rb +1 -1
- data/spec/authority/abilities_spec.rb +9 -9
- data/spec/authority/authorizer_spec.rb +20 -6
- data/spec/authority/configuration_spec.rb +1 -1
- data/spec/authority/controller_spec.rb +50 -43
- data/spec/authority/integration_spec.rb +5 -5
- data/spec/authority/user_abilities_spec.rb +10 -10
- data/spec/authority_spec.rb +18 -3
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d278f4f1f1672058635b280ec7a87a157c524a8
|
4
|
+
data.tar.gz: 8a71e78e7f76f8ae5a3d54c8dc16f1017c2bc6a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9df1e4d9c065a21bb73555fa4839830b3407cd62603e0af3fd3c2532623c0d9744dba517e38578b62ad26e8b80fd310ba353e5e2c29bb639d08e0d5717282e5e
|
7
|
+
data.tar.gz: aa3af216a21af0d250831dcf643c8f171fa64b31a5cd374270693701dc5d98eb71527675f832bfd0f440a8a8f5c6a71ef5733cde22cd3e92bd0d1c5bd3c23a5a
|
data/.travis.yml
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
3
|
- 1.9.3
|
4
|
-
- 2.0
|
5
|
-
- 2.1
|
4
|
+
- 2.0
|
5
|
+
- 2.1
|
6
|
+
- 2.2
|
6
7
|
- jruby-19mode # JRuby in 1.9 mode
|
7
|
-
- rbx
|
8
|
+
- rbx-2
|
8
9
|
|
9
10
|
gemfile:
|
10
11
|
- gemfiles/3.2.gemfile
|
11
12
|
- gemfiles/4.0.gemfile
|
12
13
|
- gemfiles/4.1.gemfile
|
14
|
+
- gemfiles/4.2.gemfile
|
13
15
|
|
14
16
|
matrix:
|
15
17
|
exclude:
|
data/CHANGELOG.markdown
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
Authority does its best to use [semantic versioning](http://semver.org).
|
4
4
|
|
5
|
+
## Unreleased
|
6
|
+
|
7
|
+
Nothing
|
8
|
+
|
9
|
+
## 3.1.0
|
10
|
+
|
11
|
+
- Allow changing the logger by updating the configuration (see commit 0214d24), based on [a question](https://github.com/nathanl/authority/issues/101) from [Nate Bird](https://github.com/natebird)
|
12
|
+
- Authorizers can now have an instance default method (`def default`), thanks to [Pascal Friederich](https://github.com/paukul)
|
13
|
+
- Fix "wrong number of arguments" bug for Sequel users, reported by [Sebastian Porto](https://github.com/sporto) in [Issue 100](https://github.com/nathanl/authority/issues/100)
|
14
|
+
|
5
15
|
## v3.0.0
|
6
16
|
|
7
17
|
Officially drop support for Ruby < 1.9.3 and Rails < 3.2.
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -10,6 +10,7 @@ If you're using it with Rails controllers, it requires that you already have som
|
|
10
10
|
[](http://travis-ci.org/nathanl/authority)
|
11
11
|
[](https://codeclimate.com/github/nathanl/authority)
|
12
12
|
[](https://gemnasium.com/nathanl/authority)
|
13
|
+
[](https://gitter.im/nathanl/authority?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
13
14
|
|
14
15
|
## Contents
|
15
16
|
|
@@ -121,6 +122,8 @@ The authorization process generally flows like this:
|
|
121
122
|
|
122
123
|
If the answer is `false` and the original caller was a controller, this is treated as a `SecurityViolation`. If it was a view, maybe you just don't show a link.
|
123
124
|
|
125
|
+
The authorization process for instances is different in that it calls the instance's `default` method before calling the class `default` method. This allows you to define default behaviour that requires access to the model instance to be determined (eg, assume any action on a blog post is allowed if that post is marked 'wiki').
|
126
|
+
|
124
127
|
(Diagrams made with [AsciiFlow](http://asciiflow.com))
|
125
128
|
|
126
129
|
<a name="installation">
|
@@ -169,7 +172,8 @@ class Article
|
|
169
172
|
# Adds `creatable_by?(user)`, etc
|
170
173
|
include Authority::Abilities
|
171
174
|
|
172
|
-
# Without this, '
|
175
|
+
# Without this, 'ArticleAuthorizer' is assumed;
|
176
|
+
# if that doesn't exist, 'ApplicationAuthorizer'
|
173
177
|
self.authorizer_name = 'AdminAuthorizer'
|
174
178
|
...
|
175
179
|
end
|
@@ -279,12 +283,12 @@ describe AdminAuthorizer do
|
|
279
283
|
end
|
280
284
|
|
281
285
|
describe "class" do
|
282
|
-
it "lets admins update
|
283
|
-
expect(AdminAuthorizer).to
|
286
|
+
it "lets admins update" do
|
287
|
+
expect(AdminAuthorizer).to be_updatable_by(@admin)
|
284
288
|
end
|
285
289
|
|
286
|
-
it "doesn't let users update
|
287
|
-
expect(AdminAuthorizer).not_to
|
290
|
+
it "doesn't let users update" do
|
291
|
+
expect(AdminAuthorizer).not_to be_updatable_by(@user)
|
288
292
|
end
|
289
293
|
end
|
290
294
|
|
@@ -313,12 +317,14 @@ end
|
|
313
317
|
|
314
318
|
If you're using Rails, ActionController support will be loaded in through a Railtie. Otherwise, you'll want to integrate it into your framework yourself. [Authority's controller](https://github.com/nathanl/authority/blob/master/lib/authority/controller.rb) is an excellent starting point.
|
315
319
|
|
316
|
-
|
320
|
+
You can check authorization in your controllers in one of two ways:
|
317
321
|
|
318
322
|
- `authorize_actions_for Llama` protects multiple controller actions with a `before_filter`, which performs a **class-level** check. If the current user is never allowed to delete a `Llama`, they'll never even get to the controller's `destroy` method.
|
319
323
|
- `authorize_action_for @llama` can be called inside a single controller action, and performs an **instance-level** check. If called inside `update`, it will check whether the current user is allowed to update this particular `@llama` instance.
|
320
324
|
|
321
|
-
|
325
|
+
If either method finds a user attempting something they're not authorized to do, a [Security Violation](#security_violations_and_logging) will result.
|
326
|
+
|
327
|
+
How does `authorize_actions_for` know to check `deletable_by?` before the controller's `destroy` action? It checks your configuration. These mappings are configurable globally from the initializer file. Defaults are as follows:
|
322
328
|
|
323
329
|
```ruby
|
324
330
|
config.controller_action_map = {
|
data/authority.gemspec
CHANGED
@@ -12,6 +12,8 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.add_dependency "activesupport", ">= 3.0.0"
|
13
13
|
gem.add_dependency "rake", ">= 0.8.7"
|
14
14
|
|
15
|
+
gem.add_development_dependency "rspec", "~> 3.3.0"
|
16
|
+
|
15
17
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
18
|
gem.files = `git ls-files`.split("\n")
|
17
19
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/gemfiles/3.2.gemfile
CHANGED
data/gemfiles/4.0.gemfile
CHANGED
data/gemfiles/4.1.gemfile
CHANGED
data/lib/authority.rb
CHANGED
data/lib/authority/authorizer.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module Authority
|
2
2
|
class Authorizer
|
3
|
-
extend Forwardable
|
4
|
-
|
5
3
|
# The base Authorizer class, from which all the authorizers in an app will
|
6
4
|
# descend. Provides the authorizer with both class and instance methods
|
7
5
|
# like `updatable_by?(user)`.
|
@@ -20,21 +18,30 @@ module Authority
|
|
20
18
|
false
|
21
19
|
end
|
22
20
|
|
23
|
-
#
|
24
|
-
|
25
|
-
|
21
|
+
# the instance default method calls the class default method
|
22
|
+
def default(adjective, user, options = {})
|
23
|
+
user_and_maybe_options = self.class.send(:user_and_maybe_options, user, options)
|
24
|
+
self.class.send(:"#{adjective}_by?", *user_and_maybe_options)
|
26
25
|
end
|
27
26
|
|
28
|
-
# Each
|
27
|
+
# Each method simply calls the `default` method (instance or class)
|
29
28
|
Authority.adjectives.each do |adjective|
|
30
29
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
31
30
|
def self.#{adjective}_by?(user, options = {})
|
32
|
-
user_and_maybe_options
|
31
|
+
default(:#{adjective}, *user_and_maybe_options(user, options))
|
32
|
+
end
|
33
|
+
|
34
|
+
def #{adjective}_by?(user, options = {})
|
35
|
+
user_and_maybe_options = self.class.send(:user_and_maybe_options, user, options)
|
33
36
|
default(:#{adjective}, *user_and_maybe_options)
|
34
37
|
end
|
35
38
|
RUBY
|
36
39
|
end
|
37
40
|
|
41
|
+
def self.user_and_maybe_options(user, options = {})
|
42
|
+
[user, options].tap {|args| args.pop if args.last == {}}
|
43
|
+
end
|
44
|
+
private_class_method :user_and_maybe_options
|
38
45
|
end
|
39
46
|
|
40
47
|
class NoAuthorizerError < StandardError ; end
|
data/lib/authority/controller.rb
CHANGED
@@ -133,12 +133,21 @@ module Authority
|
|
133
133
|
# The `before_filter` that will be setup to run when the class method
|
134
134
|
# `authorize_actions_for` is called
|
135
135
|
def run_authorization_check
|
136
|
-
|
136
|
+
if instance_authority_resource.is_a?(Array)
|
137
|
+
# Array includes options; pass as separate args
|
138
|
+
authorize_action_for(*instance_authority_resource)
|
139
|
+
else
|
140
|
+
# *resource would be interpreted as resource.to_a, which is wrong and
|
141
|
+
# actually triggers a query if it's a Sequel model
|
142
|
+
authorize_action_for(instance_authority_resource)
|
143
|
+
end
|
137
144
|
end
|
138
145
|
|
139
146
|
def instance_authority_resource
|
140
|
-
|
141
|
-
|
147
|
+
case self.class.authority_resource
|
148
|
+
when Class then self.class.authority_resource
|
149
|
+
when String, Symbol then send(self.class.authority_resource)
|
150
|
+
end
|
142
151
|
rescue NoMethodError
|
143
152
|
raise MissingResource.new(
|
144
153
|
"Trying to authorize actions for '#{self.class.authority_resource}', but can't. \
|
data/lib/authority/version.rb
CHANGED
@@ -71,13 +71,13 @@ describe Authority::Abilities do
|
|
71
71
|
|
72
72
|
it "constantizes the authorizer name as the authorizer" do
|
73
73
|
resource_class.instance_variable_set(:@authorizer, nil)
|
74
|
-
resource_class.authorizer_name.
|
74
|
+
expect(resource_class.authorizer_name).to receive(:constantize)
|
75
75
|
resource_class.authorizer
|
76
76
|
end
|
77
77
|
|
78
78
|
it "memoizes the authorizer to avoid reconstantizing" do
|
79
79
|
resource_class.authorizer
|
80
|
-
resource_class.authorizer_name.
|
80
|
+
expect(resource_class.authorizer_name).not_to receive(:constantize)
|
81
81
|
resource_class.authorizer
|
82
82
|
end
|
83
83
|
|
@@ -106,7 +106,7 @@ describe Authority::Abilities do
|
|
106
106
|
context "when given an options hash" do
|
107
107
|
|
108
108
|
it "delegates `#{method_name}` to its authorizer class, passing the options" do
|
109
|
-
resource_class.authorizer.
|
109
|
+
expect(resource_class.authorizer).to receive(method_name).with(user, :lacking => 'nothing')
|
110
110
|
resource_class.send(method_name, user, :lacking => 'nothing')
|
111
111
|
end
|
112
112
|
|
@@ -115,7 +115,7 @@ describe Authority::Abilities do
|
|
115
115
|
context "when not given an options hash" do
|
116
116
|
|
117
117
|
it "delegates `#{method_name}` to its authorizer class, passing no options" do
|
118
|
-
resource_class.authorizer.
|
118
|
+
expect(resource_class.authorizer).to receive(method_name).with(user)
|
119
119
|
resource_class.send(method_name, user)
|
120
120
|
end
|
121
121
|
|
@@ -147,8 +147,8 @@ describe Authority::Abilities do
|
|
147
147
|
context "when given an options hash" do
|
148
148
|
|
149
149
|
it "delegates `#{method_name}` to a new authorizer instance, passing the options" do
|
150
|
-
resource_class.authorizer.
|
151
|
-
@authorizer.
|
150
|
+
allow(resource_class.authorizer).to receive(:new).and_return(@authorizer)
|
151
|
+
expect(@authorizer).to receive(method_name).with(user, :with => 'mayo')
|
152
152
|
resource_instance.send(method_name, user, :with => 'mayo')
|
153
153
|
end
|
154
154
|
|
@@ -157,8 +157,8 @@ describe Authority::Abilities do
|
|
157
157
|
context "when not given an options hash" do
|
158
158
|
|
159
159
|
it "delegates `#{method_name}` to a new authorizer instance, passing no options" do
|
160
|
-
resource_class.authorizer.
|
161
|
-
@authorizer.
|
160
|
+
allow(resource_class.authorizer).to receive(:new).and_return(@authorizer)
|
161
|
+
expect(@authorizer).to receive(method_name).with(user)
|
162
162
|
resource_instance.send(method_name, user)
|
163
163
|
end
|
164
164
|
|
@@ -176,7 +176,7 @@ describe Authority::Abilities do
|
|
176
176
|
# instance of the authorizer. Otherwise, you might check, make a change to the
|
177
177
|
# model instance, check again, and get an outdated answer.
|
178
178
|
it "always creates a new authorizer instance when accessing the authorizer" do
|
179
|
-
resource_instance.class.authorizer.
|
179
|
+
expect(resource_instance.class.authorizer).to receive(:new).with(resource_instance).twice
|
180
180
|
2.times { resource_instance.authorizer }
|
181
181
|
end
|
182
182
|
|
@@ -13,6 +13,20 @@ describe Authority::Authorizer do
|
|
13
13
|
|
14
14
|
describe "instance methods" do
|
15
15
|
|
16
|
+
it "calls the instance default method if no instance method is defined" do
|
17
|
+
expect(authorizer).to receive(:default)
|
18
|
+
authorizer.creatable_by?(user)
|
19
|
+
end
|
20
|
+
|
21
|
+
context "the instance default method" do
|
22
|
+
|
23
|
+
it "delegates to the class default method" do
|
24
|
+
expect(authorizer.class).to receive(:default)
|
25
|
+
authorizer.default(:creatable, user)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
16
30
|
Authority.adjectives.each do |adjective|
|
17
31
|
method_name = "#{adjective}_by?"
|
18
32
|
|
@@ -25,7 +39,7 @@ describe Authority::Authorizer do
|
|
25
39
|
context "when given an options hash" do
|
26
40
|
|
27
41
|
it "delegates `#{method_name}` to the corresponding class method, passing the options" do
|
28
|
-
authorizer.class.
|
42
|
+
expect(authorizer.class).to receive(method_name).with(user, :under => 'God')
|
29
43
|
authorizer.send(method_name, user, :under => 'God')
|
30
44
|
end
|
31
45
|
|
@@ -34,7 +48,7 @@ describe Authority::Authorizer do
|
|
34
48
|
context "when not given an options hash" do
|
35
49
|
|
36
50
|
it "delegates `#{method_name}` to the corresponding class method, passing no options" do
|
37
|
-
authorizer.class.
|
51
|
+
expect(authorizer.class).to receive(method_name).with(user)
|
38
52
|
authorizer.send(method_name, user)
|
39
53
|
end
|
40
54
|
|
@@ -61,7 +75,7 @@ describe Authority::Authorizer do
|
|
61
75
|
|
62
76
|
it "delegates `#{method_name}` to the authorizer's `default` method, passing the options" do
|
63
77
|
able = method_name.sub('_by?', '').to_sym
|
64
|
-
Authority::Authorizer.
|
78
|
+
expect(Authority::Authorizer).to receive(:default).with(able, user, :with => 'gusto')
|
65
79
|
Authority::Authorizer.send(method_name, user, :with => 'gusto')
|
66
80
|
end
|
67
81
|
|
@@ -71,7 +85,7 @@ describe Authority::Authorizer do
|
|
71
85
|
|
72
86
|
it "delegates `#{method_name}` to the authorizer's `default` method, passing no options" do
|
73
87
|
able = method_name.sub('_by?', '').to_sym
|
74
|
-
Authority::Authorizer.
|
88
|
+
expect(Authority::Authorizer).to receive(:default).with(able, user)
|
75
89
|
Authority::Authorizer.send(method_name, user)
|
76
90
|
end
|
77
91
|
|
@@ -88,14 +102,14 @@ describe Authority::Authorizer do
|
|
88
102
|
context "when given an options hash" do
|
89
103
|
|
90
104
|
it "returns false" do
|
91
|
-
expect(Authority::Authorizer.default(:implodable, user, {:for => "my_object"})).to
|
105
|
+
expect(Authority::Authorizer.default(:implodable, user, {:for => "my_object"})).to eq(false)
|
92
106
|
end
|
93
107
|
end
|
94
108
|
|
95
109
|
context "when not given an options hash" do
|
96
110
|
|
97
111
|
it "returns false" do
|
98
|
-
expect(Authority::Authorizer.default(:implodable, user)).to
|
112
|
+
expect(Authority::Authorizer.default(:implodable, user)).to eq(false)
|
99
113
|
end
|
100
114
|
|
101
115
|
end
|
@@ -17,7 +17,7 @@ describe Authority::Configuration do
|
|
17
17
|
Authority.instance_variable_set :@configuration, nil
|
18
18
|
null = File.exists?('/dev/null') ? '/dev/null' : 'NUL:' # Allow for Windows
|
19
19
|
logger = Logger.new(null)
|
20
|
-
Logger.
|
20
|
+
expect(Logger).to receive(:new).with(STDERR).and_return(logger)
|
21
21
|
Authority.configure
|
22
22
|
Authority.logger
|
23
23
|
end
|
@@ -16,7 +16,7 @@ describe Authority::Controller do
|
|
16
16
|
context "when including" do
|
17
17
|
|
18
18
|
before :each do
|
19
|
-
Authority::Controller.
|
19
|
+
allow(Authority::Controller).to receive(:security_violation_callback).and_return(Proc.new {|exception| })
|
20
20
|
end
|
21
21
|
|
22
22
|
after :each do
|
@@ -24,7 +24,7 @@ describe Authority::Controller do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it "specifies rescuing security violations with a standard callback" do
|
27
|
-
controller_class.
|
27
|
+
expect(controller_class).to receive(:rescue_from).with(
|
28
28
|
Authority::SecurityViolation, :with => Authority::Controller.security_violation_callback
|
29
29
|
)
|
30
30
|
end
|
@@ -55,7 +55,7 @@ describe Authority::Controller do
|
|
55
55
|
# this test, so I'm stealing that behavior.
|
56
56
|
|
57
57
|
Authority.configuration.security_violation_handler = :fire_ze_missiles
|
58
|
-
controller_instance.
|
58
|
+
expect(controller_instance).to receive(:fire_ze_missiles).with(fake_exception)
|
59
59
|
controller_instance.instance_exec(fake_exception, &Authority::Controller.security_violation_callback)
|
60
60
|
|
61
61
|
end
|
@@ -101,7 +101,7 @@ describe Authority::Controller do
|
|
101
101
|
|
102
102
|
it "sets up a before_filter, passing the options it was given" do
|
103
103
|
filter_options = {:only => [:show, :edit, :update]}
|
104
|
-
controller_class.
|
104
|
+
expect(controller_class).to receive(:before_filter).with(:run_authorization_check, filter_options)
|
105
105
|
controller_class.authorize_actions_for(resource_class, filter_options)
|
106
106
|
end
|
107
107
|
|
@@ -109,12 +109,12 @@ describe Authority::Controller do
|
|
109
109
|
overridden_action_map = controller_class.authority_action_map
|
110
110
|
overridden_action_map.update(overridden_action_map) {|k,v| v = :annihilate}
|
111
111
|
child_controller.authorize_actions_for(resource_class, :all_actions => :annihilate)
|
112
|
-
child_controller.authority_action_map.
|
112
|
+
expect(child_controller.authority_action_map).to eq(overridden_action_map)
|
113
113
|
end
|
114
114
|
|
115
115
|
it "passes the action hash to the `add_actions` method" do
|
116
116
|
new_actions = {:synthesize => :create, :annihilate => 'delete'}
|
117
|
-
child_controller.
|
117
|
+
expect(child_controller).to receive(:add_actions).with(new_actions)
|
118
118
|
child_controller.authorize_actions_for(resource_class, :actions => new_actions)
|
119
119
|
end
|
120
120
|
|
@@ -190,52 +190,52 @@ describe Authority::Controller do
|
|
190
190
|
let(:controller_instance) { controller_class.new }
|
191
191
|
|
192
192
|
before(:each) do
|
193
|
-
controller_instance.
|
194
|
-
controller_instance.
|
193
|
+
allow(controller_instance).to receive(:class).and_return("FooController")
|
194
|
+
allow(controller_instance).to receive(:action_name).and_return(:bar)
|
195
195
|
end
|
196
196
|
|
197
197
|
it "sets up an after_filter, passing the options it was given" do
|
198
198
|
filter_options = {:only => [:show, :edit, :update]}
|
199
|
-
controller_class.
|
199
|
+
expect(controller_class).to receive(:after_filter).with(filter_options)
|
200
200
|
controller_class.ensure_authorization_performed(filter_options)
|
201
201
|
end
|
202
202
|
|
203
203
|
it "triggers AuthorizationNotPerformed in after filter" do
|
204
|
-
controller_class.
|
205
|
-
|
204
|
+
allow(controller_class).to receive(:after_filter).and_yield(controller_instance)
|
205
|
+
expect {
|
206
206
|
controller_class.ensure_authorization_performed
|
207
|
-
}.
|
207
|
+
}.to raise_error(Authority::Controller::AuthorizationNotPerformed)
|
208
208
|
end
|
209
209
|
|
210
210
|
it "AuthorizationNotPerformed error has meaningful message" do
|
211
|
-
controller_class.
|
212
|
-
|
211
|
+
allow(controller_class).to receive(:after_filter).and_yield(controller_instance)
|
212
|
+
expect {
|
213
213
|
controller_class.ensure_authorization_performed
|
214
|
-
}.
|
214
|
+
}.to raise_error("No authorization was performed for FooController#bar")
|
215
215
|
end
|
216
216
|
|
217
217
|
it "does not trigger AuthorizationNotPerformed when :if is false" do
|
218
|
-
controller_instance.
|
219
|
-
controller_class.
|
220
|
-
|
218
|
+
allow(controller_instance).to receive(:authorize?) { false }
|
219
|
+
allow(controller_class).to receive(:after_filter).with({}).and_yield(controller_instance)
|
220
|
+
expect {
|
221
221
|
controller_class.ensure_authorization_performed(:if => :authorize?)
|
222
|
-
}.
|
222
|
+
}.not_to raise_error()
|
223
223
|
end
|
224
224
|
|
225
225
|
it "does not trigger AuthorizationNotPerformed when :unless is true" do
|
226
|
-
controller_instance.
|
227
|
-
controller_class.
|
228
|
-
|
226
|
+
allow(controller_instance).to receive(:skip_authorization?) { true }
|
227
|
+
allow(controller_class).to receive(:after_filter).with({}).and_yield(controller_instance)
|
228
|
+
expect {
|
229
229
|
controller_class.ensure_authorization_performed(:unless => :skip_authorization?)
|
230
|
-
}.
|
230
|
+
}.not_to raise_error()
|
231
231
|
end
|
232
232
|
|
233
233
|
it "does not raise error when #authorization_performed is true" do
|
234
234
|
controller_instance.authorization_performed = true
|
235
|
-
controller_class.
|
236
|
-
|
235
|
+
allow(controller_class).to receive(:after_filter).with({}).and_yield(controller_instance)
|
236
|
+
expect {
|
237
237
|
controller_class.ensure_authorization_performed
|
238
|
-
}.
|
238
|
+
}.not_to raise_error()
|
239
239
|
end
|
240
240
|
|
241
241
|
end
|
@@ -253,7 +253,7 @@ describe Authority::Controller do
|
|
253
253
|
|
254
254
|
let(:controller_instance) do
|
255
255
|
controller_class.new.tap do |cc|
|
256
|
-
cc.
|
256
|
+
allow(cc).to receive(Authority.configuration.user_method).and_return(user)
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
@@ -264,7 +264,7 @@ describe Authority::Controller do
|
|
264
264
|
context "if a resource class was specified" do
|
265
265
|
|
266
266
|
it "checks authorization on the model specified" do
|
267
|
-
controller_instance.
|
267
|
+
expect(controller_instance).to receive(:authorize_action_for).with(resource_class)
|
268
268
|
controller_instance.send(:run_authorization_check)
|
269
269
|
end
|
270
270
|
|
@@ -284,11 +284,18 @@ describe Authority::Controller do
|
|
284
284
|
|
285
285
|
context "and the method returns a class" do
|
286
286
|
before :each do
|
287
|
-
controller_instance.
|
287
|
+
allow(controller_instance).to receive(:method_to_find_class).and_return(resource_class)
|
288
288
|
end
|
289
289
|
|
290
290
|
it "checks authorization on that class" do
|
291
|
-
controller_instance.
|
291
|
+
expect(controller_instance).to receive(:authorize_action_for).with(resource_class)
|
292
|
+
controller_instance.send(:run_authorization_check)
|
293
|
+
end
|
294
|
+
|
295
|
+
it "does not call to_a on that class" do
|
296
|
+
expect(controller_instance).to receive(:authorize_action_for).with(resource_class)
|
297
|
+
# *resource is syntactic sugar for resource.to_a
|
298
|
+
expect(resource_class).not_to receive(:to_a)
|
292
299
|
controller_instance.send(:run_authorization_check)
|
293
300
|
end
|
294
301
|
end
|
@@ -297,11 +304,11 @@ describe Authority::Controller do
|
|
297
304
|
let(:some_options) { { :a => 1, :b => 2 } }
|
298
305
|
|
299
306
|
before :each do
|
300
|
-
controller_instance.
|
307
|
+
allow(controller_instance).to receive(:method_to_find_class).and_return([resource_class, some_options])
|
301
308
|
end
|
302
309
|
|
303
310
|
it "checks authorization on that class and passes the options" do
|
304
|
-
controller_instance.
|
311
|
+
expect(controller_instance).to receive(:authorize_action_for).with(resource_class, some_options)
|
305
312
|
controller_instance.send(:run_authorization_check)
|
306
313
|
end
|
307
314
|
end
|
@@ -321,7 +328,7 @@ describe Authority::Controller do
|
|
321
328
|
end
|
322
329
|
|
323
330
|
it "raises a MissingAction if there is no corresponding action for the controller" do
|
324
|
-
controller_instance.
|
331
|
+
allow(controller_instance).to receive(:action_name).and_return('sculpt')
|
325
332
|
expect { controller_instance.send(:run_authorization_check) }.to raise_error(
|
326
333
|
Authority::Controller::MissingAction
|
327
334
|
)
|
@@ -331,23 +338,23 @@ describe Authority::Controller do
|
|
331
338
|
|
332
339
|
describe "authorize_action_for" do
|
333
340
|
|
334
|
-
before(:each) { controller_instance.
|
341
|
+
before(:each) { allow(controller_instance).to receive(:action_name).and_return(:destroy) }
|
335
342
|
|
336
343
|
it "calls Authority.enforce to authorize the action" do
|
337
|
-
Authority.
|
344
|
+
expect(Authority).to receive(:enforce)
|
338
345
|
controller_instance.send(:authorize_action_for, resource_class)
|
339
346
|
end
|
340
347
|
|
341
348
|
it "passes along any options it was given" do
|
342
349
|
options = {:for => 'insolence'}
|
343
|
-
Authority.
|
350
|
+
expect(Authority).to receive(:enforce).with('delete', resource_class, user, options)
|
344
351
|
controller_instance.send(:authorize_action_for, resource_class, options)
|
345
352
|
end
|
346
353
|
|
347
354
|
it "sets correct authorization flag" do
|
348
|
-
Authority.
|
355
|
+
allow(Authority).to receive(:enforce)
|
349
356
|
controller_instance.send(:authorize_action_for, resource_class)
|
350
|
-
controller_instance.authorization_performed
|
357
|
+
expect(controller_instance.authorization_performed?).to eq(true)
|
351
358
|
end
|
352
359
|
|
353
360
|
end
|
@@ -355,7 +362,7 @@ describe Authority::Controller do
|
|
355
362
|
describe "authority_user" do
|
356
363
|
|
357
364
|
it "gets the user for the current request from the configured user_method" do
|
358
|
-
controller_instance.
|
365
|
+
expect(controller_instance).to receive(Authority.configuration.user_method)
|
359
366
|
controller_instance.send(:authority_user)
|
360
367
|
end
|
361
368
|
|
@@ -366,15 +373,15 @@ describe Authority::Controller do
|
|
366
373
|
let(:mock_error) { double(:message => 'oh noes! an error!') }
|
367
374
|
|
368
375
|
it "logs an error" do
|
369
|
-
Authority.logger.
|
370
|
-
controller_instance.
|
376
|
+
expect(Authority.logger).to receive(:warn)
|
377
|
+
allow(controller_instance).to receive(:render)
|
371
378
|
controller_instance.send(:authority_forbidden, mock_error)
|
372
379
|
end
|
373
380
|
|
374
381
|
it "renders the public/403.html file" do
|
375
382
|
forbidden_page = Rails.root.join('public/403.html')
|
376
|
-
Authority.logger.
|
377
|
-
controller_instance.
|
383
|
+
allow(Authority.logger).to receive(:warn)
|
384
|
+
expect(controller_instance).to receive(:render).with(:file => forbidden_page, :status => 403, :layout => false)
|
378
385
|
controller_instance.send(:authority_forbidden, mock_error)
|
379
386
|
end
|
380
387
|
|
@@ -19,7 +19,7 @@ describe "integration from user through model to authorizer" do
|
|
19
19
|
describe "if given an options hash" do
|
20
20
|
|
21
21
|
it "delegates `#{adjective_method}` to its authorizer class, passing the options" do
|
22
|
-
resource_class.authorizer.
|
22
|
+
expect(resource_class.authorizer).to receive(adjective_method).with(user, :lacking => 'nothing')
|
23
23
|
user.send(verb_method, resource_class, :lacking => 'nothing')
|
24
24
|
end
|
25
25
|
|
@@ -28,7 +28,7 @@ describe "integration from user through model to authorizer" do
|
|
28
28
|
describe "if not given an options hash" do
|
29
29
|
|
30
30
|
it "delegates `#{adjective_method}` to its authorizer class, passing no options" do
|
31
|
-
resource_class.authorizer.
|
31
|
+
expect(resource_class.authorizer).to receive(adjective_method).with(user)
|
32
32
|
user.send(verb_method, resource_instance)
|
33
33
|
end
|
34
34
|
|
@@ -45,7 +45,7 @@ describe "integration from user through model to authorizer" do
|
|
45
45
|
let!(:authorizer_instance) { resource_class.authorizer.new(resource_instance) }
|
46
46
|
|
47
47
|
before :each do
|
48
|
-
resource_class.authorizer.
|
48
|
+
allow(resource_class.authorizer).to receive(:new).and_return(authorizer_instance)
|
49
49
|
end
|
50
50
|
|
51
51
|
Authority.verbs.each do |verb|
|
@@ -58,7 +58,7 @@ describe "integration from user through model to authorizer" do
|
|
58
58
|
describe "if given an options hash" do
|
59
59
|
|
60
60
|
it "delegates `#{adjective_method}` to a new authorizer instance, passing the options" do
|
61
|
-
authorizer_instance.
|
61
|
+
expect(authorizer_instance).to receive(adjective_method).with(user, :consistency => 'mushy')
|
62
62
|
user.send(verb_method, resource_instance, :consistency => 'mushy')
|
63
63
|
end
|
64
64
|
|
@@ -67,7 +67,7 @@ describe "integration from user through model to authorizer" do
|
|
67
67
|
describe "if not given an options hash" do
|
68
68
|
|
69
69
|
it "delegates `#{adjective_method}` to a new authorizer instance, passing no options" do
|
70
|
-
authorizer_instance.
|
70
|
+
expect(authorizer_instance).to receive(adjective_method).with(user)
|
71
71
|
user.send(verb_method, resource_instance)
|
72
72
|
end
|
73
73
|
|
@@ -18,7 +18,7 @@ describe Authority::UserAbilities do
|
|
18
18
|
describe "if given options" do
|
19
19
|
|
20
20
|
it "delegates the authorization check to the resource, passing the options" do
|
21
|
-
resource_instance.
|
21
|
+
expect(resource_instance).to receive("#{Authority.abilities[verb]}_by?").with(user, :size => 'wee')
|
22
22
|
user.send(method_name, resource_instance, :size => 'wee')
|
23
23
|
end
|
24
24
|
|
@@ -27,7 +27,7 @@ describe Authority::UserAbilities do
|
|
27
27
|
describe "if not given options" do
|
28
28
|
|
29
29
|
it "delegates the authorization check to the resource, passing no options" do
|
30
|
-
resource_instance.
|
30
|
+
expect(resource_instance).to receive("#{Authority.abilities[verb]}_by?").with(user)
|
31
31
|
user.send(method_name, resource_instance)
|
32
32
|
end
|
33
33
|
|
@@ -42,7 +42,7 @@ describe Authority::UserAbilities do
|
|
42
42
|
context "when ApplicationAuthorizer responds to a matching `authorizes_to?` call" do
|
43
43
|
|
44
44
|
before :each do
|
45
|
-
ApplicationAuthorizer.
|
45
|
+
allow(ApplicationAuthorizer).to receive(:authorizes_to_mimic_lemurs?).and_return('yessir')
|
46
46
|
end
|
47
47
|
|
48
48
|
it "uses the `authorizes_to` return value" do
|
@@ -50,12 +50,12 @@ describe Authority::UserAbilities do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it "passes along options if any were given" do
|
53
|
-
ApplicationAuthorizer.
|
53
|
+
expect(ApplicationAuthorizer).to receive(:authorizes_to_mimic_lemurs?).with(user, :for => :academic_credit)
|
54
54
|
user.can?(:mimic_lemurs, :for => :academic_credit)
|
55
55
|
end
|
56
56
|
|
57
57
|
it "doesn't pass along options if none were given" do
|
58
|
-
ApplicationAuthorizer.
|
58
|
+
expect(ApplicationAuthorizer).to receive(:authorizes_to_mimic_lemurs?).with(user)
|
59
59
|
user.can?(:mimic_lemurs)
|
60
60
|
end
|
61
61
|
|
@@ -64,14 +64,14 @@ describe Authority::UserAbilities do
|
|
64
64
|
context "when ApplicationAuthorizer does not respond to a matching `authorizes_to?` call" do
|
65
65
|
|
66
66
|
before :each do
|
67
|
-
ApplicationAuthorizer.
|
67
|
+
allow(ApplicationAuthorizer).to receive(:authorizes_to_mimic_lemurs?).and_raise(NoMethodError.new('eh?'))
|
68
68
|
end
|
69
69
|
|
70
70
|
context "when ApplicationAuthorizer responds to a matching `can` call" do
|
71
71
|
|
72
72
|
before :each do
|
73
|
-
ApplicationAuthorizer.
|
74
|
-
Authority.logger.
|
73
|
+
allow(ApplicationAuthorizer).to receive(:can_mimic_lemurs?).and_return('thumbs up!')
|
74
|
+
allow(Authority.logger).to receive(:warn)
|
75
75
|
end
|
76
76
|
|
77
77
|
it "uses the `can` return value (for backwards compatibility)" do
|
@@ -79,7 +79,7 @@ describe Authority::UserAbilities do
|
|
79
79
|
end
|
80
80
|
|
81
81
|
it "sends a deprecation warning" do
|
82
|
-
Authority.logger.
|
82
|
+
expect(Authority.logger).to receive(:warn).with(
|
83
83
|
"DEPRECATION WARNING: Please rename `ApplicationAuthorizer.can_mimic_lemurs?` to `authorizes_to_mimic_lemurs?`"
|
84
84
|
)
|
85
85
|
user.can?(:mimic_lemurs)
|
@@ -90,7 +90,7 @@ describe Authority::UserAbilities do
|
|
90
90
|
context "when ApplicationAuthorizer does not respond to a matching `can` call" do
|
91
91
|
|
92
92
|
before(:each) do
|
93
|
-
ApplicationAuthorizer.
|
93
|
+
allow(ApplicationAuthorizer).to receive(:can_mimic_lemurs?).and_raise(NoMethodError.new('whaaa?'))
|
94
94
|
end
|
95
95
|
|
96
96
|
it "re-raises the NoMethodError from the missing `authorizes_to?`" do
|
data/spec/authority_spec.rb
CHANGED
@@ -32,9 +32,24 @@ describe Authority do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it "requires the remainder of library internals after configuration" do
|
35
|
-
Authority.
|
35
|
+
expect(Authority).to receive(:require_authority_internals!)
|
36
36
|
Authority.configure
|
37
37
|
end
|
38
|
+
|
39
|
+
it "allows changing the logger" do
|
40
|
+
starting_config = Authority.configuration
|
41
|
+
logger1 = Object.new
|
42
|
+
logger2 = Object.new
|
43
|
+
config = Authority::Configuration.new
|
44
|
+
Authority.configuration = config
|
45
|
+
|
46
|
+
config.logger = logger1
|
47
|
+
expect(Authority.logger).to eq(logger1)
|
48
|
+
|
49
|
+
config.logger = logger2
|
50
|
+
expect(Authority.logger).to eq(logger2)
|
51
|
+
Authority.configuration = starting_config
|
52
|
+
end
|
38
53
|
end
|
39
54
|
|
40
55
|
describe "enforcement" do
|
@@ -49,7 +64,7 @@ describe Authority do
|
|
49
64
|
|
50
65
|
it "checks the user's authorization, passing along the options" do
|
51
66
|
options = { :for => 'context' }
|
52
|
-
user.
|
67
|
+
expect(user).to receive(:can_delete?).with(resource_class, options).and_return(true)
|
53
68
|
Authority.enforce(:delete, resource_class, user, options)
|
54
69
|
end
|
55
70
|
|
@@ -58,7 +73,7 @@ describe Authority do
|
|
58
73
|
describe "when not given options" do
|
59
74
|
|
60
75
|
it "checks the user's authorization, passing no options" do
|
61
|
-
user.
|
76
|
+
expect(user).to receive(:can_delete?).with(resource_class).and_return(true)
|
62
77
|
Authority.enforce(:delete, resource_class, user)
|
63
78
|
end
|
64
79
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: authority
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Long
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2015-07-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: 0.8.7
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rspec
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 3.3.0
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 3.3.0
|
42
56
|
description: Authority helps you authorize actions in your Rails app. It's ORM-neutral
|
43
57
|
and has very little fancy syntax; just group your models under one or more Authorizer
|
44
58
|
classes and write plain Ruby methods on them.
|
@@ -63,6 +77,7 @@ files:
|
|
63
77
|
- gemfiles/3.2.gemfile
|
64
78
|
- gemfiles/4.0.gemfile
|
65
79
|
- gemfiles/4.1.gemfile
|
80
|
+
- gemfiles/4.2.gemfile
|
66
81
|
- lib/authority.rb
|
67
82
|
- lib/authority/abilities.rb
|
68
83
|
- lib/authority/authorizer.rb
|