authority 2.7.0 → 2.8.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
  SHA1:
3
- metadata.gz: b3bb0dcfadb29c3e61cf717eca48de2be68259b9
4
- data.tar.gz: 5d8299cf62e1c9adabcc1c86bde6706592af9433
3
+ metadata.gz: 9206ed88d49590c996639a19863a6d31dfd96717
4
+ data.tar.gz: e17aef04639c0fab82bcb7b221167f5b066eb12d
5
5
  SHA512:
6
- metadata.gz: 9543396964cdc739ba4b41929cf5cde18de52e65c2c392f132c9b2f880c27469ceeeb6ccc6c622c4724d6458023baa981cb668440a6d3c3cab3dc7627aeadff8
7
- data.tar.gz: 9a73e9a4dee9d62b0a5a2ec4597ad3ec15293fec5eeef1379d5e2830513960b9af7729da0f8a0f46e0471662f179e1ca6b27799b753ebf811e9202753256fad2
6
+ metadata.gz: d6fb759f6188d224fe7a7a7a029a605bbe595f8ad344c9e3705d9e7d0e460b86ee5d0fb20e4d529cf22f3685f1f0c477e309301acdbde5456d9dcad5f4a32584
7
+ data.tar.gz: e762ecc99ebd2632b01a45b88ffacc19196af9eb90acb72b6063b5cef08122ae74e7d17b3921098f13cede871a6f01e2dec174aeb9e608dc0c080d37a88b6297
data/.travis.yml CHANGED
@@ -14,7 +14,6 @@ gemfile:
14
14
  - gemfiles/3.1.gemfile
15
15
  - gemfiles/3.2.gemfile
16
16
  - gemfiles/4.0.gemfile
17
- - Gemfile
18
17
 
19
18
  matrix:
20
19
  exclude:
data/CHANGELOG.markdown CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  Authority does its best to use [semantic versioning](http://semver.org).
4
4
 
5
+ ## v2.8.0
6
+
7
+ New controller method `ensure_authorization_performed`, thanks to [Igor Davydov](https://github.com/div).
8
+
5
9
  ## 2.7.0
6
10
 
7
11
  Allows setting authorizer by class (`authorizer = FooAuthorizer`) as well as by name (`authorizer_name = 'FooAuthorizer'`), thanks to [mguymon](https://github.com/mguymon)
@@ -0,0 +1,27 @@
1
+ # Contributing
2
+
3
+ ## I love you
4
+
5
+ Thanks so much for wanting to contribute to Authority! You are wonderful and I love you.
6
+
7
+ ## New feature? Probably not.
8
+
9
+ Authority's default answer to new features is "no".
10
+
11
+ Every feature increases the complexity of Authority. It will need documentation, tests, and maintenance. Its interactions with other features will forever need to be considered. More code means more chances for bugs.
12
+
13
+ Every feature makes Authority harder to understand for users and maintainers.
14
+
15
+ A feature **must clearly benefit most users** to justify these costs.
16
+
17
+ It's hard to reject pull requests. It feels like rejecting the requester personally. But the silent participants in the conversation must be considered, too: all the other users and maintainers. Every user of Authority has entrusted us to put code into their system. **We owe it to them not to add needless complexity**.
18
+
19
+ If your feature seems worth the tradeoff, great! But be aware that the bar is high.
20
+
21
+ ## Bugfixes welcome
22
+
23
+ Bugfixes, updating deprecated method calls, etc are always welcome.
24
+
25
+ ## Tests and readability
26
+
27
+ No untested code will be accepted. No confusing code will be accepted. (Yes, that's subjective.) Make it as readable as you possibly can.
data/README.markdown CHANGED
@@ -6,8 +6,10 @@ Authority will work fine with a standalone app or a single sign-on system. You c
6
6
 
7
7
  If you're using it with Rails controllers, it requires that you already have some kind of user object in your application, accessible via a method like `current_user` (configurable).
8
8
 
9
+ [![Gem Version](https://badge.fury.io/rb/authority.png)](https://rubygems.org/gems/searchlight)
9
10
  [![Build Status](https://secure.travis-ci.org/nathanl/authority.png?branch=master)](http://travis-ci.org/nathanl/authority)
10
11
  [![Code Climate](https://codeclimate.com/github/nathanl/authority.png)](https://codeclimate.com/github/nathanl/authority)
12
+ [![Dependency Status](https://gemnasium.com/nathanl/authority.png)](https://gemnasium.com/nathanl/authority)
11
13
 
12
14
  ## Contents
13
15
 
@@ -380,7 +382,7 @@ class LlamasController < ApplicationController
380
382
  end
381
383
  ```
382
384
 
383
- Finally, note that if you have a controller that dynamically determines the class it's working with, you can pass the name of a controller instance method to `authorize_actions_for` instead of a class, and the class will be looked up when a request is made.
385
+ If you have a controller that dynamically determines the class it's working with, you can pass the name of a controller instance method to `authorize_actions_for` instead of a class, and the class will be looked up when a request is made.
384
386
 
385
387
  ```ruby
386
388
  class LlamasController < ApplicationController
@@ -388,11 +390,29 @@ class LlamasController < ApplicationController
388
390
  authorize_actions_for :llama_class
389
391
 
390
392
  def llama_class
393
+ # In a real application, you would choose your llama class
394
+ # much more carefully
391
395
  [StandardLlama, LludicrousLlama].sample
392
396
  end
393
397
  end
394
398
  ```
395
399
 
400
+ Finally, you can enforce that every controller action runs an authorization check using the class method `ensure_authorization_performed`, which sets up an `after_filter` to raise an exception if it wasn't. Any `only` or `except` arguments will be passed to `after_filter`. You can also use `if` or `unless` to specify the name of a controller method which determines whether it's necessary.
401
+
402
+ Since this runs in an `after_filter`, it obviously doesn't prevent the action, it just alerts you that no authorization was performed. Therefore, it's most useful in development. An example usage might be:
403
+
404
+ ```ruby
405
+ class ApplicationController < ActionController::Base
406
+ ensure_authorization_performed :except => [:index, :search], :if => :auditing_security?, :unless => :devise_controller?
407
+
408
+ def auditing_security?
409
+ Rails.env != 'production'
410
+ end
411
+ end
412
+ ```
413
+
414
+ If you want a skippable filter, you can roll your own using the instance method, also called `ensure_authorization_performed`.
415
+
396
416
  <a name="views">
397
417
  ### Views
398
418
 
data/gemfiles/4.0.gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem "rails", ">= 4.0.0.rc", "< 4.1"
4
- gem 'rspec', '>= 2.8.0'
3
+ gem "rails", "~> 4.0"
4
+ gem 'rspec', '~> 2.14'
5
5
 
6
6
  gemspec :path=>"../"
data/lib/authority.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'active_support/concern'
2
2
  require 'active_support/core_ext/class/attribute'
3
3
  require 'active_support/core_ext/hash/keys'
4
+ require 'active_support/core_ext/hash/slice'
4
5
  require 'active_support/core_ext/string/inflections'
5
6
  require 'active_support/rescuable'
6
7
  require 'forwardable'
@@ -7,7 +7,7 @@ module Authority
7
7
 
8
8
  def self.security_violation_callback
9
9
  Proc.new do |exception|
10
- # Through the magic of ActiveSupport's `Proc#bind`, `ActionController::Base#rescue_from`
10
+ # Through the magic of `instance_exec` `ActionController::Base#rescue_from`
11
11
  # can call this proc and make `self` the actual controller instance
12
12
  self.send(Authority.configuration.security_violation_handler, exception)
13
13
  end
@@ -18,6 +18,19 @@ module Authority
18
18
  class_attribute :authority_resource, :instance_reader => false
19
19
  end
20
20
 
21
+ attr_writer :authorization_performed
22
+
23
+ def authorization_performed?
24
+ !!@authorization_performed
25
+ end
26
+
27
+ def ensure_authorization_performed(options = {})
28
+ return if authorization_performed?
29
+ return if options[:if] && !send(options[:if])
30
+ return if options[:unless] && send(options[:unless])
31
+ raise AuthorizationNotPerformed, "No authorization was performed for #{self.class.to_s}##{self.action_name}"
32
+ end
33
+
21
34
  module ClassMethods
22
35
 
23
36
  # Sets up before_filter to ensure user is allowed to perform a given controller action
@@ -49,6 +62,13 @@ module Authority
49
62
  authority_actions(action_map)
50
63
  end
51
64
 
65
+ # Convenience wrapper for instance method
66
+ def ensure_authorization_performed(options = {})
67
+ after_filter(options.slice(:only, :except)) do |controller_instance|
68
+ controller_instance.ensure_authorization_performed(options)
69
+ end
70
+ end
71
+
52
72
  # The controller action to authority action map used for determining
53
73
  # which Rails actions map to which authority actions (ex: index to read)
54
74
  #
@@ -74,7 +94,9 @@ module Authority
74
94
  if authority_action.nil?
75
95
  raise MissingAction.new("No authority action defined for #{action_name}")
76
96
  end
97
+
77
98
  Authority.enforce(authority_action, authority_resource, authority_user, *options)
99
+ self.authorization_performed = true
78
100
  end
79
101
 
80
102
  # Renders a static file to minimize the chances of further errors.
@@ -112,7 +134,8 @@ module Authority
112
134
  send(Authority.configuration.user_method)
113
135
  end
114
136
 
115
- class MissingAction < StandardError ; end
116
- class MissingResource < StandardError ; end
137
+ class MissingAction < StandardError ; end
138
+ class MissingResource < StandardError ; end
139
+ class AuthorizationNotPerformed < StandardError ; end
117
140
  end
118
141
  end
@@ -1,3 +1,3 @@
1
1
  module Authority
2
- VERSION = "2.7.0"
2
+ VERSION = "2.8.0"
3
3
  end
@@ -49,15 +49,15 @@ describe Authority::Controller do
49
49
  controller_instance = controller_class.new
50
50
  # If a callback is passed to a controller's `rescue_from` method as the value for
51
51
  # the `with` option (like `SomeController.rescue_from FooException, :with => some_callback`),
52
- # Rails will use ActiveSupport's `Proc#bind` to ensure that when the proc refers to
52
+ # Rails will use `instance_exec` to ensure that when the proc refers to
53
53
  # `self`, it will be the controller, not the proc itself.
54
54
  # I need this callback's `self` to be the controller for the purposes of
55
55
  # this test, so I'm stealing that behavior.
56
- callback = Authority::Controller.security_violation_callback.bind(controller_instance)
57
56
 
58
57
  Authority.configuration.security_violation_handler = :fire_ze_missiles
59
58
  controller_instance.should_receive(:fire_ze_missiles).with(fake_exception)
60
- callback.call(fake_exception)
59
+ controller_instance.instance_exec(fake_exception, &Authority::Controller.security_violation_callback)
60
+
61
61
  end
62
62
  end
63
63
 
@@ -155,6 +155,61 @@ describe Authority::Controller do
155
155
 
156
156
  end
157
157
 
158
+ describe "ensure_authorization_performed" do
159
+
160
+ let(:controller_instance) { controller_class.new }
161
+
162
+ before(:each) do
163
+ controller_instance.stub(:class).and_return("FooController")
164
+ controller_instance.stub(:action_name).and_return(:bar)
165
+ end
166
+
167
+ it "sets up an after_filter, passing the options it was given" do
168
+ filter_options = {:only => [:show, :edit, :update]}
169
+ controller_class.should_receive(:after_filter).with(filter_options)
170
+ controller_class.ensure_authorization_performed(filter_options)
171
+ end
172
+
173
+ it "triggers AuthorizationNotPerformed in after filter" do
174
+ controller_class.stub(:after_filter).and_yield(controller_instance)
175
+ lambda {
176
+ controller_class.ensure_authorization_performed
177
+ }.should raise_error(Authority::Controller::AuthorizationNotPerformed)
178
+ end
179
+
180
+ it "AuthorizationNotPerformed error has meaningful message" do
181
+ controller_class.stub(:after_filter).and_yield(controller_instance)
182
+ lambda {
183
+ controller_class.ensure_authorization_performed
184
+ }.should raise_error("No authorization was performed for FooController#bar")
185
+ end
186
+
187
+ it "does not trigger AuthorizationNotPerformed when :if is false" do
188
+ controller_instance.stub(:authorize?) { false }
189
+ controller_class.stub(:after_filter).with({}).and_yield(controller_instance)
190
+ lambda {
191
+ controller_class.ensure_authorization_performed(:if => :authorize?)
192
+ }.should_not raise_error()
193
+ end
194
+
195
+ it "does not trigger AuthorizationNotPerformed when :unless is true" do
196
+ controller_instance.stub(:skip_authorization?) { true }
197
+ controller_class.stub(:after_filter).with({}).and_yield(controller_instance)
198
+ lambda {
199
+ controller_class.ensure_authorization_performed(:unless => :skip_authorization?)
200
+ }.should_not raise_error()
201
+ end
202
+
203
+ it "does not raise error when #authorization_performed is true" do
204
+ controller_instance.authorization_performed = true
205
+ controller_class.stub(:after_filter).with({}).and_yield(controller_instance)
206
+ lambda {
207
+ controller_class.ensure_authorization_performed
208
+ }.should_not raise_error()
209
+ end
210
+
211
+ end
212
+
158
213
  end
159
214
 
160
215
  describe "instance methods" do
@@ -244,6 +299,12 @@ describe Authority::Controller do
244
299
  controller_instance.send(:authorize_action_for, resource_class, options)
245
300
  end
246
301
 
302
+ it "sets correct authorization flag" do
303
+ Authority.stub(:enforce)
304
+ controller_instance.send(:authorize_action_for, resource_class)
305
+ controller_instance.authorization_performed?.should be_true
306
+ end
307
+
247
308
  end
248
309
 
249
310
  describe "authority_user" do
@@ -257,7 +318,7 @@ describe Authority::Controller do
257
318
 
258
319
  describe "authority_forbidden action" do
259
320
 
260
- let(:mock_error) { mock(:message => 'oh noes! an error!') }
321
+ let(:mock_error) { double(:message => 'oh noes! an error!') }
261
322
 
262
323
  it "logs an error" do
263
324
  Authority.logger.should_receive(:warn)
@@ -66,7 +66,7 @@ describe Authority do
66
66
  end
67
67
 
68
68
  it "doesn't raise a SecurityViolation if the action is authorized" do
69
- expect { Authority.enforce(:read, resource_class, user) }.not_to raise_error(Authority::SecurityViolation)
69
+ expect { Authority.enforce(:read, resource_class, user) }.not_to raise_error()
70
70
  end
71
71
 
72
72
  end
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: 2.7.0
4
+ version: 2.8.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: 2013-07-10 00:00:00.000000000 Z
12
+ date: 2013-09-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -53,6 +53,7 @@ files:
53
53
  - .rspec
54
54
  - .travis.yml
55
55
  - CHANGELOG.markdown
56
+ - CONTRIBUTING.markdown
56
57
  - Gemfile
57
58
  - LICENSE
58
59
  - README.markdown