authority 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.markdown CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  This is mainly to document major new features and backwards-incompatible changes.
4
4
 
5
+ ## Unreleased
6
+
7
+ - Added `Authority::Authorizer.default` class method which is called before the `default_strategy` proc and delegates to that proc. This can be overridden per authorizer.
8
+
5
9
  ## v1.0.0
6
10
 
7
11
  - Added `config.security_violation_handler` so users can specify which controller method to use when rescuing `SecurityViolation`s
data/README.markdown CHANGED
@@ -57,9 +57,7 @@ Authority encapsulates all authorization logic in `Authorizer` classes. Want to
57
57
  You can group models under authorizers in any way you wish. For example:
58
58
 
59
59
 
60
- +-------------+ +--------------+ +-------------+
61
- |Simplest case| |Logical groups| |Most granular|
62
- +-------------+ +--------------+ +-------------+
60
+ Simplest case Logical groups Most granular
63
61
 
64
62
  default_strategy default_strategy default_strategy
65
63
  + + +
@@ -123,7 +121,7 @@ This option determines what methods are added to your users, models and authoriz
123
121
  # Whatever class represents a logged-in user in your app
124
122
  class User
125
123
  # Adds `can_create?(resource)`, etc
126
- include Authority::Abilities
124
+ include Authority::UserAbilities
127
125
  ...
128
126
  end
129
127
  ```
@@ -152,7 +150,8 @@ These are where your actual authorization logic goes. Here's how it works:
152
150
  - Instance methods answer questions about model instances, like "can this user update this **particular** widget?" (Within an instance method, you can get the model instance with `resource`).
153
151
  - Any instance method you don't define (for example, if you didn't make a `def deletable_by?(user)`) will fall back to the corresponding class method. In other words, if you haven't said whether a user can update **this particular** widget, we'll decide by checking whether they can update **any** widget.
154
152
  - Class methods answer questions about model classes, like "is it **ever** permissible for this user to update a Widget?"
155
- - Any class method you don't define (for example, if you didn't make a `def self.updatable_by?(user)`) will fall back to your configurable [default strategy](#default_strategies).
153
+ - Any class method you don't define (for example, if you didn't make a `def self.updatable_by?(user)`) will call that authorizer's `default` method.
154
+ - The inherited `default` method calls a [default strategy](#default_strategies) proc (**NOTE**: this will be removed in version 2.0).
156
155
 
157
156
  For example:
158
157
 
@@ -176,12 +175,15 @@ As you can see, you can specify different logic for every method on every model,
176
175
  <a name="default_strategies">
177
176
  #### Default Strategies
178
177
 
179
- Any class method you don't define on an authorizer will use your default strategy. The **default** default strategy simply returns `false`, meaning that everything is forbidden. This whitelisting approach will keep you from accidentally allowing things you didn't intend.
178
+ Any class method you don't define on an authorizer will call the `default` method on that authorizer. If you don't define **that**, the inherited `default` method from `Authority::Authorizer` will call your configured default strategy proc (**NOTE:** this proc will be removed in version 2.0; defining a `default` method is a more standard OOP approach.)
179
+
180
+ The **default** default strategy proc simply returns `false`, meaning that everything is forbidden. This whitelisting approach will keep you from accidentally allowing things you didn't intend.
180
181
 
181
182
  You can configure a different default strategy. For example, you might want one that looks up permissions in your database:
182
183
 
183
184
  ```ruby
184
185
  # In config/initializers/authority.rb
186
+ # Example args: :creatable, AdminAuthorizer, user
185
187
  config.default_strategy = Proc.new do |able, authorizer, user|
186
188
  # Does the user have any of the roles which give this permission?
187
189
  (roles_which_grant(able, authorizer) & user.roles).any?
data/TODO.markdown CHANGED
@@ -11,3 +11,4 @@
11
11
  ## Features
12
12
 
13
13
  - It would be nice to have an `authorized_link_to` method, which determines from the given path and the user's permissions whether to show the link. Not sure yet how hard this would be.
14
+ - **Breaking change**: on installation, generate empty `ApplicationAuthorizer < Authority::Authorizer`. Any model which doesn't specify its authorizer would assume `ApplicationAuthorizer` instead of `[Modelname]Authorizer`; this way, users start out with a centralized authorizer scheme instead of with the assumption that every model needs its own. This also fits the pattern of Rails controllers.
@@ -3,7 +3,7 @@ module Authority
3
3
  # Should be included into all models in a Rails app. Provides the model
4
4
  # with both class and instance methods like `updatable_by?(user)`
5
5
  # Exactly which methods get defined is determined from `config.abilities`;
6
- # the module is evaluated after any user-supplied config block is run
6
+ # the module is evaluated after any user-supplied config block is run
7
7
  # in order to make that possible.
8
8
  # All delegate to the methods of the same name on the model's authorizer.
9
9
 
@@ -30,7 +30,7 @@ module Authority
30
30
 
31
31
  def authorizer
32
32
  @authorizer ||= authorizer_name.constantize # Get an actual reference to the authorizer class
33
- rescue NameError => e
33
+ rescue NameError
34
34
  raise Authority::NoAuthorizerError.new("#{authorizer_name} does not exist in your application")
35
35
  end
36
36
  end
@@ -5,7 +5,7 @@ module Authority
5
5
  # descend. Provides the authorizer with both class and instance methods
6
6
  # like `updatable_by?(user)`.
7
7
  # Exactly which methods get defined is determined from `config.abilities`;
8
- # the class is evaluated after any user-supplied config block is run
8
+ # the class is evaluated after any user-supplied config block is run
9
9
  # in order to make that possible.
10
10
 
11
11
  attr_reader :resource
@@ -27,12 +27,16 @@ module Authority
27
27
  Authority.adjectives.each do |adjective|
28
28
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
29
29
  def self.#{adjective}_by?(user)
30
- Authority.configuration.default_strategy.call(:#{adjective}, self, user)
30
+ default(:#{adjective}, user)
31
31
  end
32
32
  RUBY
33
33
  end
34
34
 
35
+ def self.default(adjective, user)
36
+ Authority.configuration.default_strategy.call(adjective, self, user)
37
+ end
38
+
35
39
  end
36
40
 
37
- class NoAuthorizerError < StandardError ; end ;
41
+ class NoAuthorizerError < StandardError ; end
38
42
  end
@@ -9,7 +9,7 @@ module Authority
9
9
  @default_strategy = Proc.new do |able, authorizer, user|
10
10
  false
11
11
  end
12
-
12
+
13
13
 
14
14
  @abilities = {
15
15
  :create => 'creatable',
@@ -52,7 +52,7 @@ module Authority
52
52
 
53
53
  # Renders a static file to minimize the chances of further errors.
54
54
  #
55
- # @param [Exception] error, an error that indicates the user tried to perform a forbidden action.
55
+ # @param [Exception] error, an error that indicates the user tried to perform a forbidden action.
56
56
  def authority_forbidden(error)
57
57
  Authority.configuration.logger.warn(error.message)
58
58
  render :file => Rails.root.join('public', '403.html'), :status => 403, :layout => false
@@ -11,4 +11,3 @@ module Authority
11
11
 
12
12
  end
13
13
  end
14
-
@@ -4,7 +4,7 @@ module Authority
4
4
  # Should be included into whatever class represents users in an app.
5
5
  # Provides methods like `can_update?(resource)`
6
6
  # Exactly which methods get defined is determined from `config.abilities`;
7
- # the module is evaluated after any user-supplied config block is run
7
+ # the module is evaluated after any user-supplied config block is run
8
8
  # in order to make that possible.
9
9
  # All delegate to corresponding methods on the resource.
10
10
 
@@ -15,6 +15,6 @@ module Authority
15
15
  end
16
16
  RUBY
17
17
  end
18
-
18
+
19
19
  end
20
20
  end
@@ -1,3 +1,3 @@
1
1
  module Authority
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -87,11 +87,11 @@ describe Authority::Abilities do
87
87
 
88
88
  # TODO: Nathan will comment more clearly in the future
89
89
  # aka "don't memoize" (to prevent dirty models from contaminating authorization)
90
- it "should always create a new authorizer instance when accessing the authorizer" do
90
+ it "should always create a new authorizer instance when accessing the authorizer" do
91
91
  @ability_model.class.authorizer.should_receive(:new).with(@ability_model).twice
92
92
  2.times { @ability_model.authorizer }
93
93
  end
94
-
94
+
95
95
  end
96
96
 
97
97
  end
@@ -7,49 +7,59 @@ describe Authority::Authorizer do
7
7
  before :each do
8
8
  @ability_model = AbilityModel.new
9
9
  @authorizer = @ability_model.authorizer
10
- @user = User.new
10
+ @user = User.new
11
11
  end
12
12
 
13
13
  it "should take a resource instance in its initializer" do
14
14
  @authorizer.resource.should eq(@ability_model)
15
15
  end
16
16
 
17
- describe "class methods" do
17
+ describe "instance methods" do
18
18
 
19
19
  Authority.adjectives.each do |adjective|
20
20
  method_name = "#{adjective}_by?"
21
21
 
22
22
  it "should respond to `#{method_name}`" do
23
- Authority::Authorizer.should respond_to(method_name)
23
+ @authorizer.should respond_to(method_name)
24
24
  end
25
25
 
26
- it "should run the default authorization strategy block" do
27
- able = method_name.sub('_by?', '').to_sym
28
- Authority.configuration.default_strategy.should_receive(:call).with(able, Authority::Authorizer, @user)
29
- Authority::Authorizer.send(method_name, @user)
26
+ it "should delegate `#{method_name}` to the corresponding class method by default" do
27
+ @authorizer.class.should_receive(method_name).with(@user)
28
+ @authorizer.send(method_name, @user)
30
29
  end
31
30
 
32
31
  end
33
32
 
34
33
  end
35
34
 
36
- describe "instance methods" do
35
+ describe "class methods" do
37
36
 
38
37
  Authority.adjectives.each do |adjective|
39
38
  method_name = "#{adjective}_by?"
40
39
 
41
40
  it "should respond to `#{method_name}`" do
42
- @authorizer.should respond_to(method_name)
41
+ Authority::Authorizer.should respond_to(method_name)
43
42
  end
44
-
45
- it "should delegate `#{method_name}` to the corresponding class method by default" do
46
- @authorizer.class.should_receive(method_name).with(@user)
47
- @authorizer.send(method_name, @user)
43
+
44
+ it "should delegate `#{method_name}` to the authorizer's `default` method by default" do
45
+ able = method_name.sub('_by?', '').to_sym
46
+ Authority::Authorizer.should_receive(:default).with(able, @user)
47
+ Authority::Authorizer.send(method_name, @user)
48
48
  end
49
49
 
50
50
  end
51
51
 
52
52
  end
53
53
 
54
- end
54
+ describe "the default method" do
55
+
56
+ it "should call the configured `default_strategy` proc by default" do
57
+ Authority.configuration.default_strategy.should_receive(:call).with(
58
+ :implodable, Authority::Authorizer, @user
59
+ )
60
+ Authority::Authorizer.default(:implodable, @user)
61
+ end
55
62
 
63
+ end
64
+
65
+ end
@@ -35,7 +35,7 @@ describe Authority::Configuration do
35
35
  end
36
36
 
37
37
  describe "customizing the configuration" do
38
- before :all do
38
+ before :all do
39
39
  Authority.instance_variable_set :@configuration, nil
40
40
  Authority.configure do |config|
41
41
  config.abilities[:eat] = 'edible'
@@ -13,9 +13,9 @@ describe Authority::Controller do
13
13
  # Here be dragons!
14
14
  @fake_exception = Exception.new
15
15
  @sample_controller = SampleController.new
16
- # If a callback is passed to a controller's `rescue_from` method as the value for
16
+ # If a callback is passed to a controller's `rescue_from` method as the value for
17
17
  # the `with` option (like `SomeController.rescue_from FooException, :with => some_callback`),
18
- # Rails will use ActiveSupport's `Proc#bind` to ensure that when the proc refers to
18
+ # Rails will use ActiveSupport's `Proc#bind` to ensure that when the proc refers to
19
19
  # `self`, it will be the controller, not the proc itself.
20
20
  # I need this callback's `self` to be the controller for the purposes of
21
21
  # this test, so I'm stealing that behavior.
@@ -56,7 +56,7 @@ describe Authority::Controller do
56
56
  ExampleController.authority_action_map[:erase].should be_nil
57
57
  end
58
58
  end
59
-
59
+
60
60
  end
61
61
 
62
62
  describe "DSL (class) methods" do
@@ -83,7 +83,7 @@ describe Authority::Controller do
83
83
  end
84
84
 
85
85
  describe "instance methods" do
86
- before :each do
86
+ before :each do
87
87
  @user = User.new
88
88
  @controller = ExampleController.new
89
89
  @controller.stub!(:action_name).and_return(:edit)
@@ -138,4 +138,3 @@ describe Authority::Controller do
138
138
  end
139
139
 
140
140
  end
141
-
@@ -20,4 +20,3 @@ end
20
20
  class SampleController
21
21
  extend MockController
22
22
  end
23
-
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: 1.0.0
4
+ version: 1.1.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-04-17 00:00:00.000000000 Z
13
+ date: 2012-04-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
17
- requirement: &82923350 !ruby/object:Gem::Requirement
17
+ requirement: &75302770 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 3.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *82923350
25
+ version_requirements: *75302770
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: bundler
28
- requirement: &82923030 !ruby/object:Gem::Requirement
28
+ requirement: &75302460 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,7 +33,7 @@ dependencies:
33
33
  version: 1.0.0
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *82923030
36
+ version_requirements: *75302460
37
37
  description: Authority helps you authorize actions in your Rails app. It's ORM-neutral
38
38
  and has very little fancy syntax; just group your models under one or more Authorizer
39
39
  classes and write plain Ruby methods on them.