authority 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +6 -1
- data/CHANGELOG.markdown +6 -1
- data/README.markdown +64 -65
- data/Rakefile +5 -0
- data/TODO.markdown +2 -2
- data/authority.gemspec +0 -1
- data/gemfiles/3.0.gemfile +6 -0
- data/gemfiles/3.1.gemfile +6 -0
- data/gemfiles/3.2.gemfile +6 -0
- data/lib/authority.rb +2 -1
- data/lib/authority/abilities.rb +2 -3
- data/lib/authority/authorizer.rb +3 -2
- data/lib/authority/configuration.rb +5 -5
- data/lib/authority/controller.rb +2 -3
- data/lib/authority/version.rb +1 -1
- data/lib/generators/authority/install_generator.rb +11 -14
- data/lib/generators/templates/application_authorizer.rb +16 -0
- data/lib/generators/templates/authority_initializer.rb +0 -30
- data/spec/authority/abilities_spec.rb +10 -4
- data/spec/authority/authorizer_spec.rb +2 -5
- data/spec/authority/configuration_spec.rb +14 -15
- data/spec/authority/controller_spec.rb +1 -0
- data/spec/authority_spec.rb +27 -0
- data/spec/support/ability_model.rb +1 -1
- metadata +21 -18
- data/spec/support/no_authorizer_model.rb +0 -5
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.markdown
CHANGED
@@ -2,7 +2,12 @@
|
|
2
2
|
|
3
3
|
This is mainly to document major new features and backwards-incompatible changes.
|
4
4
|
|
5
|
-
##
|
5
|
+
## v2.0.0
|
6
|
+
|
7
|
+
- **Breaking change**: models now assume their authorizer is `ApplicationAuthorizer` unless told otherwise. Generator creates a blank `ApplicationAuthorizer`. This, combined with the change in v1.1.0, makes the `default_strategy` proc obsolete in favor of straightforward inheritance of a `default` method, so support for `config.default_strategy` is removed.
|
8
|
+
- Added accessors to `Authority::SecurityViolation` for user, action and resource, for use in custom security violation handlers.
|
9
|
+
|
10
|
+
## v1.1.0
|
6
11
|
|
7
12
|
- 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
13
|
|
data/README.markdown
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
Authority helps you authorize actions in your Rails app. It's **ORM-neutral** and has very little fancy syntax; just group your models under one or more Authorizer classes and write plain Ruby methods on them.
|
4
4
|
|
5
|
-
Authority will work fine with a standalone app or a single sign-on system. You can check roles in a database or permissions in a YAML file. It doesn't care! What it **does** do is give you an easy way to organize your logic
|
5
|
+
Authority will work fine with a standalone app or a single sign-on system. You can check roles in a database or permissions in a YAML file. It doesn't care! What it **does** do is give you an easy way to organize your logic and handle unauthorized actions.
|
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
9
|
[![Build Status](https://secure.travis-ci.org/nathanl/authority.png)](http://travis-ci.org/nathanl/authority)
|
10
|
+
[![Dependency Status](https://gemnasium.com/nathanl/authority.png)](https://gemnasium.com/nathanl/authority)
|
10
11
|
|
11
12
|
## Contents
|
12
13
|
|
@@ -21,9 +22,8 @@ It requires that you already have some kind of user object in your application,
|
|
21
22
|
<li><a href="#models">Models</a></li>
|
22
23
|
<li><a href="#authorizers">Authorizers</a>
|
23
24
|
<ul>
|
24
|
-
<li><a href="#
|
25
|
+
<li><a href="#default_methods">Default methods</a></li>
|
25
26
|
<li><a href="#testing_authorizers">Testing Authorizers</a></li>
|
26
|
-
<li><a href="#custom_authorizers">Custom Authorizers</a></li>
|
27
27
|
</ul></li>
|
28
28
|
<li><a href="#controllers">Controllers</a></li>
|
29
29
|
<li><a href="#views">Views</a></li>
|
@@ -54,37 +54,42 @@ The goals of Authority are:
|
|
54
54
|
|
55
55
|
Authority encapsulates all authorization logic in `Authorizer` classes. Want to do something with a model? **Ask its authorizer**.
|
56
56
|
|
57
|
-
|
57
|
+
Models that have the same authorization rules should use the same authorizer. In other words, if you would write the exact same methods on two models to determine who can create them, who can edit them, etc, then they should use the same authorizer.
|
58
58
|
|
59
|
+
Every model starts out assuming that its authorizer is `ApplicationAuthorizer`, but you can specify another one using the model's `authorizer_name=` method. Authorizers are just classes, so you can use any inheritance pattern you like.
|
60
|
+
|
61
|
+
Some example groupings:
|
59
62
|
|
60
63
|
Simplest case Logical groups Most granular
|
61
64
|
|
62
|
-
|
65
|
+
ApplicationAuthorizer ApplicationAuthorizer ApplicationAuthorizer
|
63
66
|
+ + +
|
64
67
|
| +--------+-------+ +-------------------+-------------------+
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
| + + + + +
|
69
|
+
| BasicAuthorizer AdminAuthorizer CommentAuthorizer ArticleAuthorizer EditionAuthorizer
|
70
|
+
| + + + + +
|
68
71
|
+-------+-------+ +-+ +------+ | | |
|
69
72
|
+ + + + + + + + +
|
70
73
|
Comment Article Edition Comment Article Edition Comment Article Edition
|
71
74
|
|
75
|
+
The authorization process generally flows like this:
|
72
76
|
|
73
|
-
|
74
|
-
|
75
|
-
current_user.can_create?(Moose) # You ask this question, and the user
|
77
|
+
current_user.can_create?(Article) # You ask this question, and the user
|
76
78
|
+ # automatically asks the model...
|
77
79
|
|
|
78
80
|
v
|
79
|
-
|
81
|
+
Article.creatable_by?(current_user) # The model automatically asks
|
80
82
|
+ # its authorizer...
|
81
83
|
|
|
82
84
|
v
|
83
|
-
|
84
|
-
+ # If
|
85
|
-
| #
|
85
|
+
AdminAuthorizer.creatable_by?(current_user) # *You define this method.*
|
86
|
+
+ # If you don't, the inherited one
|
87
|
+
| # calls `default`...
|
86
88
|
v
|
87
|
-
|
89
|
+
AdminAuthorizer.default(:creatable, current_user) # *You define this method.*
|
90
|
+
# If you don't, the one inherited
|
91
|
+
# from Authority::Authorizer just
|
92
|
+
# returns false.
|
88
93
|
|
89
94
|
If the answer is `false` and the original caller was a controller, this is treated as a `SecurityViolation`. If it was a view, maybe you just don't show a link.
|
90
95
|
|
@@ -109,7 +114,7 @@ config.abilities = {
|
|
109
114
|
}
|
110
115
|
```
|
111
116
|
|
112
|
-
This option determines what methods are added to your users, models and authorizers. If you need to ask `user.can_deactivate?(Satellite)` and `@satellite.deactivatable_by?(user)`, add
|
117
|
+
This option determines what methods are added to your users, models and authorizers. If you need to ask `user.can_deactivate?(Satellite)` and `@satellite.deactivatable_by?(user)`, add `:deactivate => 'deactivatable'` to the hash.
|
113
118
|
|
114
119
|
<a name="wiring_it_together">
|
115
120
|
## Wiring It Together
|
@@ -134,7 +139,7 @@ class Article
|
|
134
139
|
# Adds `creatable_by?(user)`, etc
|
135
140
|
include Authority::Abilities
|
136
141
|
|
137
|
-
# Without this, '
|
142
|
+
# Without this, 'ApplicationAuthorizer' is assumed
|
138
143
|
self.authorizer_name = 'AdminAuthorizer'
|
139
144
|
...
|
140
145
|
end
|
@@ -143,7 +148,7 @@ end
|
|
143
148
|
<a name="authorizers">
|
144
149
|
### Authorizers
|
145
150
|
|
146
|
-
Add your authorizers under `app/authorizers`, subclassing `
|
151
|
+
Add your authorizers under `app/authorizers`, subclassing the generated `ApplicationAuthorizer`.
|
147
152
|
|
148
153
|
These are where your actual authorization logic goes. Here's how it works:
|
149
154
|
|
@@ -151,13 +156,12 @@ These are where your actual authorization logic goes. Here's how it works:
|
|
151
156
|
- 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.
|
152
157
|
- Class methods answer questions about model classes, like "is it **ever** permissible for this user to update a Widget?"
|
153
158
|
- 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).
|
155
159
|
|
156
160
|
For example:
|
157
161
|
|
158
162
|
```ruby
|
159
163
|
# app/authorizers/schedule_authorizer.rb
|
160
|
-
class ScheduleAuthorizer <
|
164
|
+
class ScheduleAuthorizer < ApplicationAuthorizer
|
161
165
|
# Class method: can this user at least sometimes create a Schedule?
|
162
166
|
def self.creatable_by?(user)
|
163
167
|
user.manager?
|
@@ -168,29 +172,44 @@ class ScheduleAuthorizer < Authority::Authorizer
|
|
168
172
|
resource.in_future? && user.manager? && resource.department == user.department
|
169
173
|
end
|
170
174
|
end
|
171
|
-
```
|
172
175
|
|
173
|
-
|
176
|
+
# undefined; calls `ScheduleAuthorizer.default(:updatable, user)`
|
177
|
+
ScheduleAuthorizer.updatable_by?(user)
|
178
|
+
```
|
174
179
|
|
175
|
-
|
176
|
-
#### Default Strategies
|
180
|
+
As you can see, you can specify different logic for every method on every model, if necessary. On the other extreme, you could simply supply a [default method](#default_methods) that covers all your use cases.
|
177
181
|
|
178
|
-
|
182
|
+
<a name="default_methods">
|
183
|
+
#### Default Methods
|
179
184
|
|
180
|
-
|
185
|
+
Any class method you don't define on an authorizer will call the `default` method on that authorizer. This method is defined on `Authority::Authorizer` to simply return false. This is a 'whitelisting' approach; any permission you haven't specified (which falls back to the default method) is considered forbidden.
|
181
186
|
|
182
|
-
You can
|
187
|
+
You can override this method in your `ApplicationAuthorizer` and/or per authorizer. For example, you might want one that looks up the user's roles and correlates them with permissions:
|
183
188
|
|
184
189
|
```ruby
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
#
|
189
|
-
(
|
190
|
+
# app/authorizers/application_authorizer.rb
|
191
|
+
class ApplicationAuthorizer < Authority::Authorizer
|
192
|
+
|
193
|
+
# Example call: `default(:creatable, current_user)`
|
194
|
+
def self.default(able, user)
|
195
|
+
has_role_granting?(user, able) || user.admin?
|
196
|
+
end
|
197
|
+
|
198
|
+
protected
|
199
|
+
|
200
|
+
def has_role_granting(user, able)
|
201
|
+
# Does the user have any of the roles which give this permission?
|
202
|
+
(roles_which_grant(able) & user.roles).any?
|
203
|
+
end
|
204
|
+
|
205
|
+
def roles_which_grant(able)
|
206
|
+
# Look up roles for the current authorizer and `able`
|
207
|
+
...
|
208
|
+
end
|
190
209
|
end
|
191
210
|
```
|
192
211
|
|
193
|
-
If your system is uniform enough, **this
|
212
|
+
If your system is uniform enough, **this method alone might handle all the logic you need**.
|
194
213
|
|
195
214
|
<a name="testing_authorizers">
|
196
215
|
#### Testing Authorizers
|
@@ -223,40 +242,19 @@ describe AdminAuthorizer do
|
|
223
242
|
@admin_resource_instance = mock_admin_resource
|
224
243
|
end
|
225
244
|
|
226
|
-
it "should
|
227
|
-
@admin_resource_instance.authorizer.
|
245
|
+
it "should let admins delete" do
|
246
|
+
@admin_resource_instance.authorizer.should be_deletable_by(@admin)
|
228
247
|
end
|
229
248
|
|
230
|
-
|
231
|
-
|
232
|
-
end
|
233
|
-
```
|
234
|
-
|
235
|
-
<a name="custom_authorizers">
|
236
|
-
#### Custom Authorizers
|
237
|
-
|
238
|
-
If you want to customize your authorizers even further - for example, maybe you want them all to have a method like `has_permission?(user, permission_name)` - just use normal Ruby inheritance. For example, add your own parent class, like this:
|
239
|
-
|
240
|
-
```ruby
|
241
|
-
# lib/my_app/authorizer.rb
|
242
|
-
module MyApp
|
243
|
-
class Authorizer < Authority::Authorizer
|
244
|
-
|
245
|
-
def self.has_permission(user, permission_name)
|
246
|
-
# look that up somewhere
|
249
|
+
it "should not let users delete" do
|
250
|
+
@admin_resource_instance.authorizer.should_not be_deletable_by(@user)
|
247
251
|
end
|
248
252
|
|
249
253
|
end
|
250
|
-
end
|
251
254
|
|
252
|
-
#app/authorizers/badger_authorizer.rb
|
253
|
-
class BadgerAuthorizer < MyApp::Authorizer
|
254
|
-
# contents
|
255
255
|
end
|
256
256
|
```
|
257
257
|
|
258
|
-
If you decide to place your custom class in `lib` as shown above (as opposed to putting it in `app`), you should require it at the bottom of `config/initializers/authority.rb`.
|
259
|
-
|
260
258
|
<a name="controllers">
|
261
259
|
### Controllers
|
262
260
|
|
@@ -271,10 +269,10 @@ The relationship between controller actions and abilities - like checking `reada
|
|
271
269
|
class LlamaController < ApplicationController
|
272
270
|
|
273
271
|
# Check class-level authorizations before all actions except :create
|
274
|
-
#
|
275
|
-
authorize_actions_for Llama, :actions => {:neuter => :update},
|
272
|
+
# Also, to authorize this controller's 'neuter' action, ask whether `current_user.can_update?(Llama)`
|
273
|
+
authorize_actions_for Llama, :except => :create, :actions => {:neuter => :update},
|
276
274
|
|
277
|
-
#
|
275
|
+
# To authorize this controller's 'breed' action, ask whether `current_user.can_create?(Llama)`
|
278
276
|
authority_action :breed => 'new'
|
279
277
|
|
280
278
|
...
|
@@ -309,14 +307,15 @@ Anytime a user attempts an unauthorized action, Authority calls whatever control
|
|
309
307
|
- Renders `public/403.html`
|
310
308
|
- Logs the violation to whatever logger you configured.
|
311
309
|
|
312
|
-
You can specify a different handler like
|
310
|
+
You can specify a different handler like this:
|
313
311
|
|
314
312
|
```ruby
|
315
313
|
# config/initializers/authority.rb
|
316
|
-
...
|
317
314
|
config.security_violation_handler = :fire_ze_missiles
|
318
|
-
|
315
|
+
```
|
316
|
+
Then define the method on your controller:
|
319
317
|
|
318
|
+
```ruby
|
320
319
|
# app/controllers/application_controller.rb
|
321
320
|
class ApplicationController < ActionController::Base
|
322
321
|
|
data/Rakefile
CHANGED
data/TODO.markdown
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
-
##
|
3
|
+
## Tests
|
4
4
|
|
5
5
|
- Add tests for the generators
|
6
|
+
- Test `ActionController` integration
|
6
7
|
|
7
8
|
## Documentation
|
8
9
|
|
@@ -11,4 +12,3 @@
|
|
11
12
|
## Features
|
12
13
|
|
13
14
|
- 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/authority.gemspec
CHANGED
@@ -9,7 +9,6 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.homepage = "https://github.com/nathanl/authority"
|
10
10
|
|
11
11
|
gem.add_dependency "rails", ">= 3.0.0"
|
12
|
-
gem.add_development_dependency "bundler", ">= 1.0.0"
|
13
12
|
|
14
13
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
14
|
gem.files = `git ls-files`.split("\n")
|
data/lib/authority.rb
CHANGED
@@ -57,6 +57,8 @@ module Authority
|
|
57
57
|
end
|
58
58
|
|
59
59
|
class SecurityViolation < StandardError
|
60
|
+
attr_reader :user, :action, :resource
|
61
|
+
|
60
62
|
def initialize(user, action, resource)
|
61
63
|
@user = user
|
62
64
|
@action = action
|
@@ -74,4 +76,3 @@ require 'authority/configuration'
|
|
74
76
|
require 'authority/controller'
|
75
77
|
require 'authority/railtie' if defined?(Rails)
|
76
78
|
require 'authority/version'
|
77
|
-
|
data/lib/authority/abilities.rb
CHANGED
@@ -10,11 +10,10 @@ module Authority
|
|
10
10
|
module Abilities
|
11
11
|
extend ActiveSupport::Concern
|
12
12
|
|
13
|
-
#
|
14
|
-
# (but let the user change that)
|
13
|
+
# Assume authorizer is `ApplicationAuthorizer` (but let the user change that)
|
15
14
|
included do
|
16
15
|
class_attribute :authorizer_name
|
17
|
-
self.authorizer_name = "
|
16
|
+
self.authorizer_name = "ApplicationAuthorizer"
|
18
17
|
end
|
19
18
|
|
20
19
|
module ClassMethods
|
data/lib/authority/authorizer.rb
CHANGED
@@ -23,7 +23,7 @@ module Authority
|
|
23
23
|
RUBY
|
24
24
|
end
|
25
25
|
|
26
|
-
# Each class method simply calls the
|
26
|
+
# Each class method simply calls the `default` method
|
27
27
|
Authority.adjectives.each do |adjective|
|
28
28
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
29
29
|
def self.#{adjective}_by?(user)
|
@@ -32,8 +32,9 @@ module Authority
|
|
32
32
|
RUBY
|
33
33
|
end
|
34
34
|
|
35
|
+
# Whitelisting approach: anything not specified will be forbidden
|
35
36
|
def self.default(adjective, user)
|
36
|
-
|
37
|
+
false
|
37
38
|
end
|
38
39
|
|
39
40
|
end
|
@@ -3,13 +3,9 @@ module Authority
|
|
3
3
|
|
4
4
|
# Has default settings, overrideable in the initializer.
|
5
5
|
|
6
|
-
attr_accessor :
|
6
|
+
attr_accessor :abilities, :controller_action_map, :user_method, :security_violation_handler, :logger
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
@default_strategy = Proc.new do |able, authorizer, user|
|
10
|
-
false
|
11
|
-
end
|
12
|
-
|
13
9
|
|
14
10
|
@abilities = {
|
15
11
|
:create => 'creatable',
|
@@ -35,5 +31,9 @@ module Authority
|
|
35
31
|
@logger = Logger.new(STDERR)
|
36
32
|
end
|
37
33
|
|
34
|
+
def default_strategy=(val)
|
35
|
+
raise ArgumentError, "`config.default_strategy=` was removed in Authority 2.0; see README and CHANGELOG"
|
36
|
+
end
|
37
|
+
|
38
38
|
end
|
39
39
|
end
|
data/lib/authority/controller.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module Authority
|
2
|
+
# Gets included into the app's controllers automatically by the railtie
|
2
3
|
module Controller
|
3
4
|
|
4
|
-
# Gets included into the app's controllers automatically by the railtie
|
5
|
-
|
6
5
|
extend ActiveSupport::Concern
|
7
6
|
|
8
7
|
included do
|
@@ -66,7 +65,7 @@ module Authority
|
|
66
65
|
authorize_action_for self.class.authority_resource
|
67
66
|
end
|
68
67
|
|
69
|
-
#
|
68
|
+
# Convenience wrapper for sending configured user_method to extract the
|
70
69
|
# request's current user
|
71
70
|
#
|
72
71
|
# @return [Object] the user object returned from sending the user_method
|
data/lib/authority/version.rb
CHANGED
@@ -8,22 +8,15 @@ module Authority
|
|
8
8
|
desc "Creates an Authority initializer for your application."
|
9
9
|
|
10
10
|
def do_all
|
11
|
+
create_authorizers_directory
|
12
|
+
copy_application_authorizer
|
11
13
|
copy_initializer
|
12
14
|
copy_forbidden
|
13
|
-
create_authorizers_directory
|
14
15
|
message = <<-RUBY
|
15
16
|
|
16
17
|
Install complete! See the README on Github for instructions on getting your
|
17
18
|
app running with Authority.
|
18
19
|
|
19
|
-
One note: each model needs to know the name of its its authorizer class.
|
20
|
-
You can specify that in the model like `authorizer_name FooAuthorizer`.
|
21
|
-
If you don't, the `Article` model (for example) will look for `ArticleAuthorizer`.
|
22
|
-
|
23
|
-
To generate one authorizer like that for each of your models, see
|
24
|
-
`rails g authority:authorizers`. If you also want to specify your own
|
25
|
-
parent class for them, use `rails g authority:authorizers MyClass`.
|
26
|
-
|
27
20
|
RUBY
|
28
21
|
puts message.strip_heredoc
|
29
22
|
|
@@ -31,6 +24,15 @@ module Authority
|
|
31
24
|
|
32
25
|
private
|
33
26
|
|
27
|
+
def create_authorizers_directory
|
28
|
+
# creates empty directory if none; doesn't empty the directory
|
29
|
+
empty_directory "app/authorizers"
|
30
|
+
end
|
31
|
+
|
32
|
+
def copy_application_authorizer
|
33
|
+
template "application_authorizer.rb", "app/authorizers/application_authorizer.rb"
|
34
|
+
end
|
35
|
+
|
34
36
|
def copy_initializer
|
35
37
|
template "authority_initializer.rb", "config/initializers/authority.rb"
|
36
38
|
end
|
@@ -39,11 +41,6 @@ module Authority
|
|
39
41
|
template "403.html", "public/403.html"
|
40
42
|
end
|
41
43
|
|
42
|
-
def create_authorizers_directory
|
43
|
-
# creates empty directory if none; doesn't empty the directory
|
44
|
-
empty_directory "app/authorizers"
|
45
|
-
end
|
46
|
-
|
47
44
|
end
|
48
45
|
end
|
49
46
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Other authorizers should subclass this one
|
2
|
+
class ApplicationAuthorizer < Authority::Authorizer
|
3
|
+
|
4
|
+
# Any class method from Authority::Authorizer that isn't overridden
|
5
|
+
# will call its authorizer's default method.
|
6
|
+
#
|
7
|
+
# @param [Symbol] adjective; example: `:creatable`
|
8
|
+
# @param [Object] user - whatever represents the current user in your app
|
9
|
+
# @return [Boolean]
|
10
|
+
def self.default(adjective, user)
|
11
|
+
# 'Whitelist' strategy for security: anything not explicitly allowed is
|
12
|
+
# considered forbidden.
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -8,35 +8,6 @@ Authority.configure do |config|
|
|
8
8
|
# Default is:
|
9
9
|
#
|
10
10
|
# config.user_method = :current_user
|
11
|
-
|
12
|
-
# DEFAULT_STRATEGY
|
13
|
-
# ================
|
14
|
-
# When no class-level method is defined on an Authorizer, a default_strategy
|
15
|
-
# proc will be called to determine what to do.
|
16
|
-
# Depending on your app, you may be able to put all the logic you need here.
|
17
|
-
#
|
18
|
-
# The arguments passed to this proc will be:
|
19
|
-
#
|
20
|
-
# able - symbol name of 'able', like `:creatable`, or `:deletable`
|
21
|
-
# authorizer - authorizer constant. Ex: `WidgetAuthorizer` or `UserAuthorizer`
|
22
|
-
# user - user object (whatever that is in your application; found using config.user_method)
|
23
|
-
#
|
24
|
-
# For example:
|
25
|
-
#
|
26
|
-
# config.default_strategy = Proc.new { |able, authorizer, user|
|
27
|
-
# # Does the user have any of the roles which give this permission?
|
28
|
-
# (roles_which_grant(able, authorizer) & user.roles).any?
|
29
|
-
# }
|
30
|
-
#
|
31
|
-
# OR
|
32
|
-
#
|
33
|
-
# config.default_strategy = Proc.new { |able, authorizer, user|
|
34
|
-
# able != :implodable && user.has_hairstyle?('pompadour')
|
35
|
-
# }
|
36
|
-
#
|
37
|
-
# Default default strategy simply returns false, as follows:
|
38
|
-
#
|
39
|
-
# config.default_strategy = Proc.new { |able, authorizer, user| false }
|
40
11
|
|
41
12
|
# CONTROLLER_ACTION_MAP
|
42
13
|
# For a given controller method, what verb must a user be able to do?
|
@@ -93,4 +64,3 @@ Authority.configure do |config|
|
|
93
64
|
# config.logger = Logger.new('/dev/null') # Don't log at all (on a Unix system)
|
94
65
|
|
95
66
|
end
|
96
|
-
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'support/ability_model'
|
3
|
-
require 'support/no_authorizer_model'
|
4
3
|
require 'support/user'
|
5
4
|
|
6
5
|
describe Authority::Abilities do
|
@@ -19,8 +18,8 @@ describe Authority::Abilities do
|
|
19
18
|
AbilityModel.should respond_to(:authorizer_name=)
|
20
19
|
end
|
21
20
|
|
22
|
-
it "should have a default authorizer_name of '
|
23
|
-
AbilityModel.authorizer_name.should eq("
|
21
|
+
it "should have a default authorizer_name of 'ApplicationAuthorizer'" do
|
22
|
+
AbilityModel.authorizer_name.should eq("ApplicationAuthorizer")
|
24
23
|
end
|
25
24
|
|
26
25
|
it "should constantize the authorizer name as the authorizer" do
|
@@ -36,7 +35,14 @@ describe Authority::Abilities do
|
|
36
35
|
end
|
37
36
|
|
38
37
|
it "should raise a friendly error if the authorizer doesn't exist" do
|
39
|
-
|
38
|
+
AbilityModel.instance_variable_set(:@authorizer, nil)
|
39
|
+
AbilityModel.authorizer_name = 'NonExistentAuthorizer'
|
40
|
+
expect { AbilityModel.authorizer }.to raise_error(Authority::NoAuthorizerError)
|
41
|
+
|
42
|
+
# Cleanup to prevent affecting other tests
|
43
|
+
# TODO: Clean up this cleanup code. :)
|
44
|
+
AbilityModel.instance_variable_set(:@authorizer, nil)
|
45
|
+
AbilityModel.authorizer_name = 'ApplicationAuthorizer'
|
40
46
|
end
|
41
47
|
|
42
48
|
end
|
@@ -53,11 +53,8 @@ describe Authority::Authorizer do
|
|
53
53
|
|
54
54
|
describe "the default method" do
|
55
55
|
|
56
|
-
it "should
|
57
|
-
Authority.
|
58
|
-
:implodable, Authority::Authorizer, @user
|
59
|
-
)
|
60
|
-
Authority::Authorizer.default(:implodable, @user)
|
56
|
+
it "should return false" do
|
57
|
+
Authority::Authorizer.default(:implodable, @user).should be_false
|
61
58
|
end
|
62
59
|
|
63
60
|
end
|
@@ -3,14 +3,6 @@ require 'spec_helper'
|
|
3
3
|
describe Authority::Configuration do
|
4
4
|
describe "the default configuration" do
|
5
5
|
|
6
|
-
it "should have a default authorization strategy block" do
|
7
|
-
Authority.configuration.default_strategy.should respond_to(:call)
|
8
|
-
end
|
9
|
-
|
10
|
-
it "should return false when calling the default authorization strategy block" do
|
11
|
-
Authority.configuration.default_strategy.call(:action, Authority::Authorizer, User.new).should be_false
|
12
|
-
end
|
13
|
-
|
14
6
|
it "should have a default authority controller actions map" do
|
15
7
|
Authority.configuration.controller_action_map.should be_a(Hash)
|
16
8
|
end
|
@@ -39,9 +31,6 @@ describe Authority::Configuration do
|
|
39
31
|
Authority.instance_variable_set :@configuration, nil
|
40
32
|
Authority.configure do |config|
|
41
33
|
config.abilities[:eat] = 'edible'
|
42
|
-
config.default_strategy = Proc.new { |able, authorizer, user|
|
43
|
-
true
|
44
|
-
}
|
45
34
|
end
|
46
35
|
|
47
36
|
after :all do
|
@@ -49,10 +38,6 @@ describe Authority::Configuration do
|
|
49
38
|
Authority.configure
|
50
39
|
end
|
51
40
|
|
52
|
-
it "should allow customizing the authorization block" do
|
53
|
-
Authority.configuration.default_strategy.call(:action, Authority::Authorizer, User.new).should be_true
|
54
|
-
end
|
55
|
-
|
56
41
|
# This shouldn't be used during runtime, only during configuration
|
57
42
|
# It won't do anything outside of configuration anyway
|
58
43
|
it "should allow adding to the default list of abilities" do
|
@@ -61,4 +46,18 @@ describe Authority::Configuration do
|
|
61
46
|
|
62
47
|
end
|
63
48
|
end
|
49
|
+
|
50
|
+
describe "helping those upgrading to 2.0" do
|
51
|
+
|
52
|
+
before :all do
|
53
|
+
Authority.instance_variable_set :@configuration, nil
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should raise a helpful exception if `config.default_strategy` is called" do
|
57
|
+
expect { Authority.configure { |config| config.default_strategy = Proc.new { false }}}.to raise_error(
|
58
|
+
ArgumentError, "`config.default_strategy=` was removed in Authority 2.0; see README and CHANGELOG"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
64
63
|
end
|
@@ -130,6 +130,7 @@ describe Authority::Controller do
|
|
130
130
|
|
131
131
|
it "should render the public/403.html file" do
|
132
132
|
forbidden_page = Rails.root.join('public/403.html')
|
133
|
+
Authority.configuration.logger.stub(:warn)
|
133
134
|
@controller.should_receive(:render).with(:file => forbidden_page, :status => 403, :layout => false)
|
134
135
|
@controller.send(:authority_forbidden, @mock_error)
|
135
136
|
end
|
data/spec/authority_spec.rb
CHANGED
@@ -54,4 +54,31 @@ describe Authority do
|
|
54
54
|
|
55
55
|
end
|
56
56
|
|
57
|
+
describe Authority::SecurityViolation do
|
58
|
+
|
59
|
+
before :each do
|
60
|
+
@user = "I am a user"
|
61
|
+
@action = :keelhaul
|
62
|
+
@resource = "I am a resource"
|
63
|
+
@security_violation = Authority::SecurityViolation.new(@user, @action, @resource)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should have a reader for the user" do
|
67
|
+
@security_violation.user.should eq(@user)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should have a reader for the action" do
|
71
|
+
@security_violation.action.should eq(@action)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should have a reader for the resource" do
|
75
|
+
@security_violation.resource.should eq(@resource)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should use them all in its message" do
|
79
|
+
@security_violation.message.should eq("#{@user} is not authorized to #{@action} this resource: #{@resource}")
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
57
84
|
end
|
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:
|
4
|
+
version: 2.0.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-30 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
17
|
-
requirement: &
|
17
|
+
requirement: &2152144680 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,18 +22,7 @@ dependencies:
|
|
22
22
|
version: 3.0.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
26
|
-
- !ruby/object:Gem::Dependency
|
27
|
-
name: bundler
|
28
|
-
requirement: &75302460 !ruby/object:Gem::Requirement
|
29
|
-
none: false
|
30
|
-
requirements:
|
31
|
-
- - ! '>='
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.0.0
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: *75302460
|
25
|
+
version_requirements: *2152144680
|
37
26
|
description: Authority helps you authorize actions in your Rails app. It's ORM-neutral
|
38
27
|
and has very little fancy syntax; just group your models under one or more Authorizer
|
39
28
|
classes and write plain Ruby methods on them.
|
@@ -55,6 +44,9 @@ files:
|
|
55
44
|
- Rakefile
|
56
45
|
- TODO.markdown
|
57
46
|
- authority.gemspec
|
47
|
+
- gemfiles/3.0.gemfile
|
48
|
+
- gemfiles/3.1.gemfile
|
49
|
+
- gemfiles/3.2.gemfile
|
58
50
|
- lib/authority.rb
|
59
51
|
- lib/authority/abilities.rb
|
60
52
|
- lib/authority/authorizer.rb
|
@@ -65,6 +57,7 @@ files:
|
|
65
57
|
- lib/authority/version.rb
|
66
58
|
- lib/generators/authority/install_generator.rb
|
67
59
|
- lib/generators/templates/403.html
|
60
|
+
- lib/generators/templates/application_authorizer.rb
|
68
61
|
- lib/generators/templates/authority_initializer.rb
|
69
62
|
- spec/authority/abilities_spec.rb
|
70
63
|
- spec/authority/authorizer_spec.rb
|
@@ -76,7 +69,6 @@ files:
|
|
76
69
|
- spec/support/ability_model.rb
|
77
70
|
- spec/support/example_controllers.rb
|
78
71
|
- spec/support/mock_rails.rb
|
79
|
-
- spec/support/no_authorizer_model.rb
|
80
72
|
- spec/support/user.rb
|
81
73
|
homepage: https://github.com/nathanl/authority
|
82
74
|
licenses: []
|
@@ -98,9 +90,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
90
|
version: '0'
|
99
91
|
requirements: []
|
100
92
|
rubyforge_project:
|
101
|
-
rubygems_version: 1.8.
|
93
|
+
rubygems_version: 1.8.16
|
102
94
|
signing_key:
|
103
95
|
specification_version: 3
|
104
96
|
summary: Authority helps you authorize actions in your Rails app using plain Ruby
|
105
97
|
methods on Authorizer classes.
|
106
|
-
test_files:
|
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
|