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 +4 -0
- data/README.markdown +8 -6
- data/TODO.markdown +1 -0
- data/lib/authority/abilities.rb +2 -2
- data/lib/authority/authorizer.rb +7 -3
- data/lib/authority/configuration.rb +1 -1
- data/lib/authority/controller.rb +1 -1
- data/lib/authority/railtie.rb +0 -1
- data/lib/authority/user_abilities.rb +2 -2
- data/lib/authority/version.rb +1 -1
- data/spec/authority/abilities_spec.rb +2 -2
- data/spec/authority/authorizer_spec.rb +24 -14
- data/spec/authority/configuration_spec.rb +1 -1
- data/spec/authority/controller_spec.rb +4 -5
- data/spec/support/example_controllers.rb +0 -1
- metadata +6 -6
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::
|
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
|
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
|
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.
|
data/lib/authority/abilities.rb
CHANGED
@@ -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
|
33
|
+
rescue NameError
|
34
34
|
raise Authority::NoAuthorizerError.new("#{authorizer_name} does not exist in your application")
|
35
35
|
end
|
36
36
|
end
|
data/lib/authority/authorizer.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/authority/controller.rb
CHANGED
@@ -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
|
data/lib/authority/railtie.rb
CHANGED
@@ -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
|
data/lib/authority/version.rb
CHANGED
@@ -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
|
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 "
|
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
|
-
|
23
|
+
@authorizer.should respond_to(method_name)
|
24
24
|
end
|
25
25
|
|
26
|
-
it "should
|
27
|
-
|
28
|
-
|
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 "
|
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
|
-
|
41
|
+
Authority::Authorizer.should respond_to(method_name)
|
43
42
|
end
|
44
|
-
|
45
|
-
it "should delegate `#{method_name}` to the
|
46
|
-
|
47
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
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.
|
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-
|
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: &
|
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: *
|
25
|
+
version_requirements: *75302770
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: bundler
|
28
|
-
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: *
|
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.
|