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 CHANGED
@@ -15,3 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ *.swp
19
+ *.swo
@@ -7,4 +7,9 @@ rvm:
7
7
  - jruby-19mode # JRuby in 1.9 mode
8
8
  - rbx-18mode
9
9
  - rbx-19mode
10
- script: rspec spec
10
+
11
+ gemfile:
12
+ - gemfiles/3.0.gemfile
13
+ - gemfiles/3.1.gemfile
14
+ - gemfiles/3.2.gemfile
15
+ - Gemfile
@@ -2,7 +2,12 @@
2
2
 
3
3
  This is mainly to document major new features and backwards-incompatible changes.
4
4
 
5
- ## Unreleased
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
 
@@ -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, define a default strategy, and handle unauthorized actions.
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="#default_strategies">Default strategies</a></li>
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
- You can group models under authorizers in any way you wish. For example:
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
- default_strategy default_strategy default_strategy
65
+ ApplicationAuthorizer ApplicationAuthorizer ApplicationAuthorizer
63
66
  + + +
64
67
  | +--------+-------+ +-------------------+-------------------+
65
- + + + + + +
66
- EverythingAuthorizer BasicAuthorizer AdminAuthorizer CommentAuthorizer ArticleAuthorizer EditionAuthorizer
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
- The process generally flows like this:
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
- Moose.creatable_by?(current_user) # The model automatically asks
81
+ Article.creatable_by?(current_user) # The model automatically asks
80
82
  + # its authorizer...
81
83
  |
82
84
  v
83
- MooseAuthorizer.creatable_by?(current_user) # *You define this method.*
84
- + # If it's missing, the default
85
- | # strategy is used...
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
- config.default_strategy.call(:creatable, MooseAuthorizer, user) # *You define this strategy.*
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 those to the hash.
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, 'ArticleAuthorizer' is assumed
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 `Authority::Authorizer`.
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 < Authority::Authorizer
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
- 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 strategy](#default_strategies) that covers all your use cases.
176
+ # undefined; calls `ScheduleAuthorizer.default(:updatable, user)`
177
+ ScheduleAuthorizer.updatable_by?(user)
178
+ ```
174
179
 
175
- <a name="default_strategies">
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
- 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.)
182
+ <a name="default_methods">
183
+ #### Default Methods
179
184
 
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.
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 configure a different default strategy. For example, you might want one that looks up permissions in your database:
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
- # In config/initializers/authority.rb
186
- # Example args: :creatable, AdminAuthorizer, user
187
- config.default_strategy = Proc.new do |able, authorizer, user|
188
- # Does the user have any of the roles which give this permission?
189
- (roles_which_grant(able, authorizer) & user.roles).any?
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 strategy alone might handle all the logic you need**.
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 not allow users to delete" do
227
- @admin_resource_instance.authorizer.should_not be_deletable_by(@user)
245
+ it "should let admins delete" do
246
+ @admin_resource_instance.authorizer.should be_deletable_by(@admin)
228
247
  end
229
248
 
230
- end
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
- # Before this controller's 'neuter' action, ask whether current_user.can_update?(Llama)
275
- authorize_actions_for Llama, :actions => {:neuter => :update}, :except => :create
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
- # Before this controller's 'breed' action, ask whether current_user.can_create?(Llama)
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 so:
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
@@ -1,2 +1,7 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task "default" => "spec"
@@ -1,8 +1,9 @@
1
1
  # TODO
2
2
 
3
- ## Chores
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.
@@ -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")
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.0.12"
4
+ gem 'rspec', '>= 2.8.0'
5
+
6
+ gemspec :path=>"../"
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.1.4"
4
+ gem 'rspec', '>= 2.8.0'
5
+
6
+ gemspec :path=>"../"
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.2.3"
4
+ gem 'rspec', '>= 2.8.0'
5
+
6
+ gemspec :path=>"../"
@@ -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
-
@@ -10,11 +10,10 @@ module Authority
10
10
  module Abilities
11
11
  extend ActiveSupport::Concern
12
12
 
13
- # Let the Foo model know that its authorizer is called 'FooAuthorizer'
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 = "#{name}Authorizer"
16
+ self.authorizer_name = "ApplicationAuthorizer"
18
17
  end
19
18
 
20
19
  module ClassMethods
@@ -23,7 +23,7 @@ module Authority
23
23
  RUBY
24
24
  end
25
25
 
26
- # Each class method simply calls the user-definable default strategy
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
- Authority.configuration.default_strategy.call(adjective, self, user)
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 :default_strategy, :abilities, :controller_action_map, :user_method, :security_violation_handler, :logger
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
@@ -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
- # Convencience wrapper for sending configured user_method to extract the
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
@@ -1,3 +1,3 @@
1
1
  module Authority
2
- VERSION = "1.1.0"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -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 '(ClassName)Authorizer'" do
23
- AbilityModel.authorizer_name.should eq("AbilityModelAuthorizer")
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
- expect { NoAuthorizerModel.authorizer }.to raise_error(Authority::NoAuthorizerError)
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 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)
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
@@ -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
@@ -2,7 +2,7 @@ class AbilityModel
2
2
  include Authority::Abilities
3
3
  end
4
4
 
5
- class AbilityModelAuthorizer < Authority::Authorizer
5
+ class ApplicationAuthorizer < Authority::Authorizer
6
6
  def self.readable_by?(user)
7
7
  true
8
8
  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: 1.1.0
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-24 00:00:00.000000000 Z
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: &75302770 !ruby/object:Gem::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: *75302770
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.10
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
@@ -1,5 +0,0 @@
1
- # No corresponding Authorizer is defined for this model
2
-
3
- class NoAuthorizerModel
4
- include Authority::Abilities
5
- end