authority 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
 
3
- This is mainly to document major new features and backwards-incompatible changes.
3
+ ## v2.2.0
4
+
5
+ Allow passing options hash to `authorize_action_for`, like `authorize_action_for(@llama, :sporting => @hat_style)`.
6
+
7
+ ## v2.1.0
8
+
9
+ Allow passing options hash, like `current_user.can_create?(Comment, :for => @post)`.
4
10
 
5
11
  ## v2.0.1
6
12
 
@@ -6,7 +6,7 @@ Authority will work fine with a standalone app or a single sign-on system. You c
6
6
 
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
- [![Build Status](https://secure.travis-ci.org/nathanl/authority.png)](http://travis-ci.org/nathanl/authority)
9
+ [![Build Status](https://secure.travis-ci.org/nathanl/authority.png?branch=master)](http://travis-ci.org/nathanl/authority)
10
10
  [![Dependency Status](https://gemnasium.com/nathanl/authority.png)](https://gemnasium.com/nathanl/authority)
11
11
 
12
12
  ## Contents
@@ -52,13 +52,13 @@ Using Authority, you have:
52
52
 
53
53
  Most importantly, you have **total flexibility**: Authority does not constrain you into using a particular scheme of roles and/or permissions.
54
54
 
55
- Authority lets you:
55
+ Authority lets you control access based on:
56
56
 
57
- - Grant permissions based on users' points (like StackOverflow)
58
- - Check user roles in a separate, single-sign-on app
59
- - Disallow certain actions based on time or date
60
- - Allow an action only for users with compatible browsers
61
- - ...do anything else you can think of.
57
+ - Roles in your app's database ([rolify](http://github.com/EppO/rolify) makes this easy)
58
+ - Roles in a separate, single-sign-on app
59
+ - Users' points (like StackOverflow)
60
+ - Time and date
61
+ - Weather, stock prices, vowels in the user's name, or **anything else you can check with Ruby**
62
62
 
63
63
  All you have to do is define the methods you need on your authorizers. You have all the flexibility of normal Ruby classes.
64
64
 
@@ -347,6 +347,8 @@ class LlamasController < ApplicationController
347
347
  end
348
348
  ```
349
349
 
350
+ As with other authorization checks, you can also pass options here, and they'll be sent along to your authorization method: `authorize_action_for(@llama, :sporting => @hat_style)`. Generally, though, your authorization will depend on some attribute or association of the model instance, so the authorizer can check `@llama.neck_strength` and `@llama.owner.nationality`, etc, without needing any additional information.
351
+
350
352
  <a name="views">
351
353
  ### Views
352
354
 
@@ -366,7 +368,18 @@ Anytime a user attempts an unauthorized action, Authority calls whatever control
366
368
  - Renders `public/403.html`
367
369
  - Logs the violation to whatever logger you configured.
368
370
 
369
- You can specify a different handler like this:
371
+ You can define your own `authority_forbidden` method:
372
+
373
+
374
+ ```ruby
375
+ # Send 'em back where they came from with a slap on the wrist
376
+ def authority_forbidden(exception)
377
+ Authority.configuration.logger.warn(error.message)
378
+ redirect_to request.referrer.presence || root_path, :alert => 'You are not authorized to complete that action.'
379
+ end
380
+ ```
381
+
382
+ ... or specify a different handler like this:
370
383
 
371
384
  ```ruby
372
385
  # config/initializers/authority.rb
@@ -26,13 +26,17 @@ module Authority
26
26
  # @param [Symbol] action
27
27
  # @param [Model] resource instance
28
28
  # @param [User] user instance
29
+ # @param [Hash] options, arbitrary options hash to delegate to the authorizer
29
30
  # @raise [SecurityViolation] if user is not allowed to perform action on resource
30
31
  # @return [Model] resource instance
31
- def self.enforce(action, resource, user)
32
- action_authorized = user.send("can_#{action}?", resource)
33
- unless action_authorized
34
- raise SecurityViolation.new(user, action, resource)
35
- end
32
+ def self.enforce(action, resource, user, *options)
33
+ action_authorized = if options.empty?
34
+ user.send("can_#{action}?", resource)
35
+ else
36
+ user.send("can_#{action}?", resource, Hash[*options])
37
+ end
38
+ raise SecurityViolation.new(user, action, resource) unless action_authorized
39
+
36
40
  resource
37
41
  end
38
42
 
@@ -74,15 +74,18 @@ module Authority
74
74
  end
75
75
 
76
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.
77
79
  #
78
- # @param authority_resource [Class], the model class associated with this controller
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
79
82
  # @raise [MissingAction] if controller action isn't a key in `config.controller_action_map`
80
- def authorize_action_for(authority_resource)
83
+ def authorize_action_for(authority_resource, *options)
81
84
  authority_action = self.class.authority_action_map[action_name.to_sym]
82
85
  if authority_action.nil?
83
86
  raise MissingAction.new("No authority action defined for #{action_name}")
84
87
  end
85
- Authority.enforce(authority_action, authority_resource, authority_user)
88
+ Authority.enforce(authority_action, authority_resource, authority_user, *options)
86
89
  end
87
90
 
88
91
  class MissingAction < StandardError ; end
@@ -1,3 +1,3 @@
1
1
  module Authority
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -64,7 +64,7 @@ Authority.configure do |config|
64
64
  #
65
65
  # Some possible settings:
66
66
  # config.logger = Rails.logger # Log with all your app's other messages
67
- # config.logger = Logger.new('logs/authority.log') # Use this file
67
+ # config.logger = Logger.new('log/authority.log') # Use this file
68
68
  # config.logger = Logger.new('/dev/null') # Don't log at all (on a Unix system)
69
69
 
70
70
  end
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'support/ability_model'
2
+ require 'support/example_model'
3
3
  require 'support/user'
4
4
 
5
5
  describe Authority::Abilities do
@@ -11,31 +11,31 @@ describe Authority::Abilities do
11
11
  describe "authorizer" do
12
12
 
13
13
  it "should have a class attribute getter for authorizer_name" do
14
- AbilityModel.should respond_to(:authorizer_name)
14
+ ExampleModel.should respond_to(:authorizer_name)
15
15
  end
16
16
 
17
17
  it "should have a class attribute setter for authorizer_name" do
18
- AbilityModel.should respond_to(:authorizer_name=)
18
+ ExampleModel.should respond_to(:authorizer_name=)
19
19
  end
20
20
 
21
21
  it "should have a default authorizer_name of 'ApplicationAuthorizer'" do
22
- AbilityModel.authorizer_name.should eq("ApplicationAuthorizer")
22
+ ExampleModel.authorizer_name.should eq("ApplicationAuthorizer")
23
23
  end
24
24
 
25
25
  it "should constantize the authorizer name as the authorizer" do
26
- AbilityModel.instance_variable_set(:@authorizer, nil)
27
- AbilityModel.authorizer_name.should_receive(:constantize)
28
- AbilityModel.authorizer
26
+ ExampleModel.instance_variable_set(:@authorizer, nil)
27
+ ExampleModel.authorizer_name.should_receive(:constantize)
28
+ ExampleModel.authorizer
29
29
  end
30
30
 
31
31
  it "should memoize the authorizer to avoid reconstantizing" do
32
- AbilityModel.authorizer
33
- AbilityModel.authorizer_name.should_not_receive(:constantize)
34
- AbilityModel.authorizer
32
+ ExampleModel.authorizer
33
+ ExampleModel.authorizer_name.should_not_receive(:constantize)
34
+ ExampleModel.authorizer
35
35
  end
36
36
 
37
37
  it "should raise a friendly error if the authorizer doesn't exist" do
38
- class NoAuthorizerModel < AbilityModel; end ;
38
+ class NoAuthorizerModel < ExampleModel; end ;
39
39
  NoAuthorizerModel.instance_variable_set(:@authorizer, nil)
40
40
  NoAuthorizerModel.authorizer_name = 'NonExistentAuthorizer'
41
41
  expect { NoAuthorizerModel.authorizer }.to raise_error(Authority::NoAuthorizerError)
@@ -49,14 +49,14 @@ describe Authority::Abilities do
49
49
  method_name = "#{adjective}_by?"
50
50
 
51
51
  it "should respond to `#{method_name}`" do
52
- AbilityModel.should respond_to(method_name)
52
+ ExampleModel.should respond_to(method_name)
53
53
  end
54
54
 
55
55
  describe "if given an options hash" do
56
56
 
57
57
  it "should delegate `#{method_name}` to its authorizer class, passing the options" do
58
- AbilityModel.authorizer.should_receive(method_name).with(@user, :lacking => 'nothing')
59
- AbilityModel.send(method_name, @user, :lacking => 'nothing')
58
+ ExampleModel.authorizer.should_receive(method_name).with(@user, :lacking => 'nothing')
59
+ ExampleModel.send(method_name, @user, :lacking => 'nothing')
60
60
  end
61
61
 
62
62
  end
@@ -64,8 +64,8 @@ describe Authority::Abilities do
64
64
  describe "if not given an options hash" do
65
65
 
66
66
  it "should delegate `#{method_name}` to its authorizer class, passing no options" do
67
- AbilityModel.authorizer.should_receive(method_name).with(@user)
68
- AbilityModel.send(method_name, @user)
67
+ ExampleModel.authorizer.should_receive(method_name).with(@user)
68
+ ExampleModel.send(method_name, @user)
69
69
  end
70
70
 
71
71
  end
@@ -77,23 +77,23 @@ describe Authority::Abilities do
77
77
  describe "instance methods" do
78
78
 
79
79
  before :each do
80
- @ability_model = AbilityModel.new
81
- @authorizer = AbilityModel.authorizer.new(@ability_model)
80
+ @example_model = ExampleModel.new
81
+ @authorizer = ExampleModel.authorizer.new(@example_model)
82
82
  end
83
83
 
84
84
  Authority.adjectives.each do |adjective|
85
85
  method_name = "#{adjective}_by?"
86
86
 
87
87
  it "should respond to `#{method_name}`" do
88
- @ability_model.should respond_to(method_name)
88
+ @example_model.should respond_to(method_name)
89
89
  end
90
90
 
91
91
  describe "if given an options hash" do
92
92
 
93
93
  it "should delegate `#{method_name}` to a new authorizer instance, passing the options" do
94
- AbilityModel.authorizer.stub(:new).and_return(@authorizer)
94
+ ExampleModel.authorizer.stub(:new).and_return(@authorizer)
95
95
  @authorizer.should_receive(method_name).with(@user, :with => 'mayo')
96
- @ability_model.send(method_name, @user, :with => 'mayo')
96
+ @example_model.send(method_name, @user, :with => 'mayo')
97
97
  end
98
98
 
99
99
  end
@@ -101,9 +101,9 @@ describe Authority::Abilities do
101
101
  describe "if not given an options hash" do
102
102
 
103
103
  it "should delegate `#{method_name}` to a new authorizer instance, passing no options" do
104
- AbilityModel.authorizer.stub(:new).and_return(@authorizer)
104
+ ExampleModel.authorizer.stub(:new).and_return(@authorizer)
105
105
  @authorizer.should_receive(method_name).with(@user)
106
- @ability_model.send(method_name, @user)
106
+ @example_model.send(method_name, @user)
107
107
  end
108
108
 
109
109
  end
@@ -111,15 +111,15 @@ describe Authority::Abilities do
111
111
  end
112
112
 
113
113
  it "should provide an accessor for its authorizer" do
114
- @ability_model.should respond_to(:authorizer)
114
+ @example_model.should respond_to(:authorizer)
115
115
  end
116
116
 
117
117
  # When checking instance methods, we want to ensure that every check uses a new
118
118
  # instance of the authorizer. Otherwise, you might check, make a change to the
119
119
  # model instance, check again, and get an outdated answer.
120
120
  it "should always create a new authorizer instance when accessing the authorizer" do
121
- @ability_model.class.authorizer.should_receive(:new).with(@ability_model).twice
122
- 2.times { @ability_model.authorizer }
121
+ @example_model.class.authorizer.should_receive(:new).with(@example_model).twice
122
+ 2.times { @example_model.authorizer }
123
123
  end
124
124
 
125
125
  end
@@ -1,17 +1,17 @@
1
1
  require 'spec_helper'
2
- require 'support/ability_model'
2
+ require 'support/example_model'
3
3
  require 'support/user'
4
4
 
5
5
  describe Authority::Authorizer do
6
6
 
7
7
  before :each do
8
- @ability_model = AbilityModel.new
9
- @authorizer = @ability_model.authorizer
8
+ @example_model = ExampleModel.new
9
+ @authorizer = @example_model.authorizer
10
10
  @user = User.new
11
11
  end
12
12
 
13
13
  it "should take a resource instance in its initializer" do
14
- @authorizer.resource.should eq(@ability_model)
14
+ @authorizer.resource.should eq(@example_model)
15
15
  end
16
16
 
17
17
  describe "instance methods" do
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'support/ability_model'
2
+ require 'support/example_model'
3
3
  require 'support/example_controllers'
4
4
  require 'support/mock_rails'
5
5
  require 'support/user'
@@ -61,18 +61,18 @@ describe Authority::Controller do
61
61
 
62
62
  describe "DSL (class) methods" do
63
63
  it "should allow specifying the model to protect" do
64
- ExampleController.authorize_actions_for AbilityModel
65
- ExampleController.authority_resource.should eq(AbilityModel)
64
+ ExampleController.authorize_actions_for ExampleModel
65
+ ExampleController.authority_resource.should eq(ExampleModel)
66
66
  end
67
67
 
68
68
  it "should pass the options provided to the before filter that is set up" do
69
69
  @options = {:only => [:show, :edit, :update]}
70
70
  ExampleController.should_receive(:before_filter).with(:run_authorization_check, @options)
71
- ExampleController.authorize_actions_for AbilityModel, @options
71
+ ExampleController.authorize_actions_for ExampleModel, @options
72
72
  end
73
73
 
74
74
  it "should allow specifying the authority action map in the `authorize_actions_for` declaration" do
75
- ExampleController.authorize_actions_for AbilityModel, :actions => {:eat => 'delete'}
75
+ ExampleController.authorize_actions_for ExampleModel, :actions => {:eat => 'delete'}
76
76
  ExampleController.authority_action_map[:eat].should eq('delete')
77
77
  end
78
78
 
@@ -91,10 +91,16 @@ describe Authority::Controller do
91
91
  end
92
92
 
93
93
  it "should check authorization on the model specified" do
94
- @controller.should_receive(:authorize_action_for).with(AbilityModel)
94
+ @controller.should_receive(:authorize_action_for).with(ExampleModel)
95
95
  @controller.send(:run_authorization_check)
96
96
  end
97
97
 
98
+ it "should pass the options provided to `authorize_action_for` downstream" do
99
+ @controller.stub!(:action_name).and_return(:destroy)
100
+ Authority.should_receive(:enforce).with('delete', ExampleModel, @user, :for => 'context')
101
+ @controller.send(:authorize_action_for, ExampleModel, :for => 'context')
102
+ end
103
+
98
104
  it "should raise a SecurityViolation if authorization fails" do
99
105
  expect { @controller.send(:run_authorization_check) }.to raise_error(Authority::SecurityViolation)
100
106
  end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+ require 'support/example_model'
3
+ require 'support/user'
4
+
5
+ describe "integration from user through model to authorizer" do
6
+
7
+ before :each do
8
+ @user = User.new
9
+ @example_model = ExampleModel.new
10
+ end
11
+
12
+ describe "class methods" do
13
+
14
+ Authority.verbs.each do |verb|
15
+ verb_method = "can_#{verb}?"
16
+ adjective = Authority.abilities[verb]
17
+ adjective_method = "#{adjective}_by?"
18
+
19
+ describe "if given an options hash" do
20
+
21
+ it "should delegate `#{adjective_method}` to its authorizer class, passing the options" do
22
+ ExampleModel.authorizer.should_receive(adjective_method).with(@user, :lacking => 'nothing')
23
+ @user.send(verb_method, ExampleModel, :lacking => 'nothing')
24
+ end
25
+
26
+ end
27
+
28
+ describe "if not given an options hash" do
29
+
30
+ it "should delegate `#{adjective_method}` to its authorizer class, passing no options" do
31
+ ExampleModel.authorizer.should_receive(adjective_method).with(@user)
32
+ @user.send(verb_method, @example_model)
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ describe "instance methods" do
42
+
43
+ before :each do
44
+ @user = User.new
45
+ @example_model = ExampleModel.new
46
+ @authorizer = ExampleModel.authorizer.new(@example_model)
47
+ ExampleModel.authorizer.stub(:new).and_return(@authorizer)
48
+ end
49
+
50
+ Authority.verbs.each do |verb|
51
+ verb_method = "can_#{verb}?"
52
+ adjective = Authority.abilities[verb]
53
+ adjective_method = "#{adjective}_by?"
54
+
55
+ describe "if given an options hash" do
56
+
57
+ it "should delegate `#{adjective_method}` to a new authorizer instance, passing the options" do
58
+ @authorizer.should_receive(adjective_method).with(@user, :consistency => 'mushy')
59
+ @user.send(verb_method, @example_model, :consistency => 'mushy')
60
+ end
61
+
62
+ end
63
+
64
+ describe "if not given an options hash" do
65
+
66
+ it "should delegate `#{adjective_method}` to a new authorizer instance, passing no options" do
67
+ @authorizer.should_receive(adjective_method).with(@user)
68
+ @user.send(verb_method, @example_model)
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
- require 'support/ability_model'
2
+ require 'support/example_model'
3
3
  require 'support/user'
4
4
 
5
5
  describe Authority::UserAbilities do
6
6
 
7
7
  before :each do
8
- @ability_model = AbilityModel.new
8
+ @example_model = ExampleModel.new
9
9
  @user = User.new
10
10
  end
11
11
 
@@ -19,8 +19,8 @@ describe Authority::UserAbilities do
19
19
  describe "if given options" do
20
20
 
21
21
  it "should delegate the authorization check to the resource, passing the options" do
22
- @ability_model.should_receive("#{Authority.abilities[verb]}_by?").with(@user, :size => 'wee')
23
- @user.send(method_name, @ability_model, :size => 'wee')
22
+ @example_model.should_receive("#{Authority.abilities[verb]}_by?").with(@user, :size => 'wee')
23
+ @user.send(method_name, @example_model, :size => 'wee')
24
24
  end
25
25
 
26
26
  end
@@ -28,8 +28,8 @@ describe Authority::UserAbilities do
28
28
  describe "if not given options" do
29
29
 
30
30
  it "should delegate the authorization check to the resource, passing no options" do
31
- @ability_model.should_receive("#{Authority.abilities[verb]}_by?").with(@user)
32
- @user.send(method_name, @ability_model)
31
+ @example_model.should_receive("#{Authority.abilities[verb]}_by?").with(@user)
32
+ @user.send(method_name, @example_model)
33
33
  end
34
34
 
35
35
  end
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'support/ability_model'
2
+ require 'support/example_model'
3
3
  require 'support/user'
4
4
 
5
5
  describe Authority do
@@ -44,12 +44,31 @@ describe Authority do
44
44
  @user = User.new
45
45
  end
46
46
 
47
+ describe "if given options" do
48
+
49
+ it "should check the user's authorization, passing along the options" do
50
+ options = { :for => 'context' }
51
+ @user.should_receive(:can_delete?).with(ExampleModel, options).and_return(true)
52
+ Authority.enforce(:delete, ExampleModel, @user, options)
53
+ end
54
+
55
+ end
56
+
57
+ describe "if not given options" do
58
+
59
+ it "should check the user's authorization, passing no options" do
60
+ @user.should_receive(:can_delete?).with(ExampleModel).and_return(true)
61
+ Authority.enforce(:delete, ExampleModel, @user)
62
+ end
63
+
64
+ end
65
+
47
66
  it "should raise a SecurityViolation if the action is unauthorized" do
48
- expect { Authority.enforce(:update, AbilityModel, @user) }.to raise_error(Authority::SecurityViolation)
67
+ expect { Authority.enforce(:update, ExampleModel, @user) }.to raise_error(Authority::SecurityViolation)
49
68
  end
50
69
 
51
70
  it "should not raise a SecurityViolation if the action is authorized" do
52
- expect { Authority.enforce(:read, AbilityModel, @user) }.not_to raise_error(Authority::SecurityViolation)
71
+ expect { Authority.enforce(:read, ExampleModel, @user) }.not_to raise_error(Authority::SecurityViolation)
53
72
  end
54
73
 
55
74
  end
@@ -1,4 +1,4 @@
1
- class AbilityModel
1
+ class ExampleModel
2
2
  include Authority::Abilities
3
3
  end
4
4
 
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.1.0
4
+ version: 2.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-08-10 00:00:00.000000000 Z
13
+ date: 2012-10-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
17
- requirement: &2160582080 !ruby/object:Gem::Requirement
17
+ requirement: &81456900 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,7 +22,7 @@ dependencies:
22
22
  version: 3.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2160582080
25
+ version_requirements: *81456900
26
26
  description: Authority helps you authorize actions in your Rails app. It's ORM-neutral
27
27
  and has very little fancy syntax; just group your models under one or more Authorizer
28
28
  classes and write plain Ruby methods on them.
@@ -63,11 +63,12 @@ files:
63
63
  - spec/authority/authorizer_spec.rb
64
64
  - spec/authority/configuration_spec.rb
65
65
  - spec/authority/controller_spec.rb
66
+ - spec/authority/integration_spec.rb
66
67
  - spec/authority/user_abilities_spec.rb
67
68
  - spec/authority_spec.rb
68
69
  - spec/spec_helper.rb
69
- - spec/support/ability_model.rb
70
70
  - spec/support/example_controllers.rb
71
+ - spec/support/example_model.rb
71
72
  - spec/support/mock_rails.rb
72
73
  - spec/support/user.rb
73
74
  homepage: https://github.com/nathanl/authority
@@ -90,20 +91,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
91
  version: '0'
91
92
  requirements: []
92
93
  rubyforge_project:
93
- rubygems_version: 1.8.16
94
+ rubygems_version: 1.8.10
94
95
  signing_key:
95
96
  specification_version: 3
96
97
  summary: Authority helps you authorize actions in your Rails app using plain Ruby
97
98
  methods on Authorizer classes.
98
- test_files:
99
- - spec/authority/abilities_spec.rb
100
- - spec/authority/authorizer_spec.rb
101
- - spec/authority/configuration_spec.rb
102
- - spec/authority/controller_spec.rb
103
- - spec/authority/user_abilities_spec.rb
104
- - spec/authority_spec.rb
105
- - spec/spec_helper.rb
106
- - spec/support/ability_model.rb
107
- - spec/support/example_controllers.rb
108
- - spec/support/mock_rails.rb
109
- - spec/support/user.rb
99
+ test_files: []