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 +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.
|