authority 1.0.0 → 1.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.
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.