Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 449 lines (328 sloc) 19.859 kb
3a24300 @nathanl renamed gem to 'authority'
authored
1 # Authority
7e96a6b @nathanl initial commit
authored
2
fd5db87 @nathanl Updated project description
authored
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
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
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.
4a4b9e7 @nathanl More README updates
authored
6
379841a @nathanl Tiny README tweaks
authored
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).
4a4b9e7 @nathanl More README updates
authored
8
2a90c32 @nathanl Fix badge [ci skip]
authored
9 [![Build Status](https://secure.travis-ci.org/nathanl/authority.png?branch=master)](http://travis-ci.org/nathanl/authority)
10 [![Dependency Status](https://gemnasium.com/nathanl/authority.png)](https://gemnasium.com/nathanl/authority)
88986f8 @nathanl Note on build statuses
authored
11
e1ab32e @nathanl Massive README reorg
authored
12 ## Contents
13
2eaa868 @nathanl Resort to HTML; Markdown fails on 3-deep lists?
authored
14 <ul>
15 <li><a href="#overview">Overview</a></li>
16 <li><a href="#flow_of_authority">The flow of Authority</a></li>
17 <li><a href="#installation">Installation</a></li>
18 <li><a href="#defining_your_abilities">Defining Your Abilities</a></li>
19 <li><a href="#wiring_it_together">Wiring It Together</a>
20 <ul>
21 <li><a href="#users">Users</a></li>
22 <li><a href="#models">Models</a></li>
23 <li><a href="#authorizers">Authorizers</a>
24 <ul>
dad9ccf @nathanl Updated README to show options
authored
25 <li><a href="#passing_options">Passing Options</a></li>
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
26 <li><a href="#default_methods">Default methods</a></li>
2eaa868 @nathanl Resort to HTML; Markdown fails on 3-deep lists?
authored
27 <li><a href="#testing_authorizers">Testing Authorizers</a></li>
28 </ul></li>
29 <li><a href="#controllers">Controllers</a></li>
30 <li><a href="#views">Views</a></li>
31 </ul></li>
32 <li><a href="#security_violations_and_logging">Security Violations &amp; Logging</a></li>
33 <li><a href="#credits">Credits</a></li>
34 <li><a href="#contributing">Contributing</a></li>
35 </ul>
e1ab32e @nathanl Massive README reorg
authored
36
37 <a name="overview">
1b4be57 @nathanl First pass at docs
authored
38 ## Overview
39
8d03dde @nathanl README update
authored
40 Using Authority, you have:
1b4be57 @nathanl First pass at docs
authored
41
8d03dde @nathanl README update
authored
42 - Broad, **class-level** rules. Examples:
9d6607d @nathanl Documentation corrections
authored
43 - "Basic users cannot delete any Widget."
1b4be57 @nathanl First pass at docs
authored
44 - "Only admin users can create Offices."
8d03dde @nathanl README update
authored
45 - Fine-grained, **instance-level** rules. Examples:
9d6607d @nathanl Documentation corrections
authored
46 - "Management users can only edit schedules with date ranges in the future."
1b4be57 @nathanl First pass at docs
authored
47 - "Users can't create playlists more than 20 songs long unless they've paid."
c417d57 @nathanl Long-overdue nautical example [ci skip]
authored
48 - A clear syntax for permissions-based views. Examples:
9d6607d @nathanl Documentation corrections
authored
49 - `link_to 'Edit Widget', edit_widget_path(@widget) if current_user.can_update?(@widget)`
c417d57 @nathanl Long-overdue nautical example [ci skip]
authored
50 - `link_to 'Keelhaul Scallywag', keelhaul_scallywag_path(@scallywag) if current_user.can_keelhaul?(@scallywag)`
8d03dde @nathanl README update
authored
51 - Graceful handling of access violations: by default, it displays a "you can't do that" screen and logs the violation.
52 - Minimal effort and mess.
53
54 Most importantly, you have **total flexibility**: Authority does not constrain you into using a particular scheme of roles and/or permissions.
55
fc50837 @nathanl Add link to rolify
authored
56 Authority lets you control access based on:
8d03dde @nathanl README update
authored
57
fc50837 @nathanl Add link to rolify
authored
58 - Roles in your app's database ([rolify](http://github.com/EppO/rolify) makes this easy)
59 - Roles in a separate, single-sign-on app
60 - Users' points (like StackOverflow)
61 - Time and date
62 - Weather, stock prices, vowels in the user's name, or **anything else you can check with Ruby**
8d03dde @nathanl README update
authored
63
64 All you have to do is define the methods you need on your authorizers. You have all the flexibility of normal Ruby classes.
65
66 **You** make the rules; Authority enforces them.
7e96a6b @nathanl initial commit
authored
67
e1ab32e @nathanl Massive README reorg
authored
68 <a name="flow_of_authority">
95f9dd1 @nathanl Update README
authored
69 ## The flow of Authority
70
379841a @nathanl Tiny README tweaks
authored
71 Authority encapsulates all authorization logic in `Authorizer` classes. Want to do something with a model? **Ask its authorizer**.
95f9dd1 @nathanl Update README
authored
72
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
73 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.
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
74
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
75 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.
76
77 Some example groupings:
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
78
fe7142d @nathanl Aesthetic graph label change
authored
79 Simplest case Logical groups Most granular
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
80
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
81 ApplicationAuthorizer ApplicationAuthorizer ApplicationAuthorizer
73ffdd6 @nathanl Slight diagram skinnyfication
authored
82 + + +
83 | +--------+-------+ +-------------------+-------------------+
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
84 | + + + + +
85 | BasicAuthorizer AdminAuthorizer CommentAuthorizer ArticleAuthorizer EditionAuthorizer
86 | + + + + +
73ffdd6 @nathanl Slight diagram skinnyfication
authored
87 +-------+-------+ +-+ +------+ | | |
88 + + + + + + + + +
89 Comment Article Edition Comment Article Edition Comment Article Edition
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
90
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
91 The authorization process generally flows like this:
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
92
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
93 current_user.can_create?(Article) # You ask this question, and the user
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
94 + # automatically asks the model...
95 |
96 v
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
97 Article.creatable_by?(current_user) # The model automatically asks
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
98 + # its authorizer...
99 |
100 v
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
101 AdminAuthorizer.creatable_by?(current_user) # *You define this method.*
102 + # If you don't, the inherited one
103 | # calls `default`...
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
104 v
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
105 AdminAuthorizer.default(:creatable, current_user) # *You define this method.*
8c6dd66 @nathanl README tweaks for clarity (thanks, @michaeltherobot)
authored
106 # If you don't, it will use the one
107 # inherited from ApplicationAuthorizer.
108 # (Its parent, Authority::Authorizer,
109 # defines the method as `return false`.)
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
110
111 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.
112
113 (Diagrams made with [AsciiFlow](http://asciiflow.com))
95f9dd1 @nathanl Update README
authored
114
e1ab32e @nathanl Massive README reorg
authored
115 <a name="installation">
7e96a6b @nathanl initial commit
authored
116 ## Installation
117
e1ab32e @nathanl Massive README reorg
authored
118 Starting from a clean commit status, add `authority` to your Gemfile, `bundle`, then `rails g authority:install`.
7e96a6b @nathanl initial commit
authored
119
e1ab32e @nathanl Massive README reorg
authored
120 <a name="defining_your_abilities">
121 ## Defining Your Abilities
7e96a6b @nathanl initial commit
authored
122
e1ab32e @nathanl Massive README reorg
authored
123 Edit `config/initializers/authority.rb`. That file documents all your options, but one of particular interest is `config.abilities`, which defines the verbs and corresponding adjectives in your app. The defaults are:
7e96a6b @nathanl initial commit
authored
124
1deedd4 @nathanl Will Github highlight this nicely?
authored
125 ```ruby
3ce2f2f @nathanl use Github-flavored markdown
authored
126 config.abilities = {
127 :create => 'creatable',
128 :read => 'readable',
129 :update => 'updatable',
130 :delete => 'deletable'
131 }
1deedd4 @nathanl Will Github highlight this nicely?
authored
132 ```
c92e4d9 @nathanl More info in generator template and more docs
authored
133
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
134 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.
c92e4d9 @nathanl More info in generator template and more docs
authored
135
e1ab32e @nathanl Massive README reorg
authored
136 <a name="wiring_it_together">
137 ## Wiring It Together
7e96a6b @nathanl initial commit
authored
138
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
139 <a name="users">
1b4be57 @nathanl First pass at docs
authored
140 ### Users
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
141
1deedd4 @nathanl Will Github highlight this nicely?
authored
142 ```ruby
3ce2f2f @nathanl use Github-flavored markdown
authored
143 # Whatever class represents a logged-in user in your app
144 class User
145 # Adds `can_create?(resource)`, etc
a8757a1 @nathanl Typo - thanks to @byu for pointing it out
authored
146 include Authority::UserAbilities
3ce2f2f @nathanl use Github-flavored markdown
authored
147 ...
148 end
1deedd4 @nathanl Will Github highlight this nicely?
authored
149 ```
4a4b9e7 @nathanl More README updates
authored
150
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
151 <a name="models">
f6acb7a @nathanl buncha pending tests
authored
152 ### Models
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
153
1deedd4 @nathanl Will Github highlight this nicely?
authored
154 ```ruby
3ce2f2f @nathanl use Github-flavored markdown
authored
155 class Article
156 # Adds `creatable_by?(user)`, etc
157 include Authority::Abilities
158
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
159 # Without this, 'ApplicationAuthorizer' is assumed
3ce2f2f @nathanl use Github-flavored markdown
authored
160 self.authorizer_name = 'AdminAuthorizer'
161 ...
162 end
1deedd4 @nathanl Will Github highlight this nicely?
authored
163 ```
1b4be57 @nathanl First pass at docs
authored
164
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
165 <a name="authorizers">
1b4be57 @nathanl First pass at docs
authored
166 ### Authorizers
167
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
168 Add your authorizers under `app/authorizers`, subclassing the generated `ApplicationAuthorizer`.
1b4be57 @nathanl First pass at docs
authored
169
e1ab32e @nathanl Massive README reorg
authored
170 These are where your actual authorization logic goes. Here's how it works:
1b4be57 @nathanl First pass at docs
authored
171
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
172 - 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`).
173 - 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.
e1ab32e @nathanl Massive README reorg
authored
174 - Class methods answer questions about model classes, like "is it **ever** permissible for this user to update a Widget?"
f0c47ce @nathanl README now discusses methods
authored
175 - 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.
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
176
e1ab32e @nathanl Massive README reorg
authored
177 For example:
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
178
1deedd4 @nathanl Will Github highlight this nicely?
authored
179 ```ruby
3ce2f2f @nathanl use Github-flavored markdown
authored
180 # app/authorizers/schedule_authorizer.rb
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
181 class ScheduleAuthorizer < ApplicationAuthorizer
3ce2f2f @nathanl use Github-flavored markdown
authored
182 # Class method: can this user at least sometimes create a Schedule?
183 def self.creatable_by?(user)
184 user.manager?
185 end
186
187 # Instance method: can this user delete this particular schedule?
188 def deletable_by?(user)
189 resource.in_future? && user.manager? && resource.department == user.department
190 end
191 end
1b4be57 @nathanl First pass at docs
authored
192
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
193 # undefined; calls `ScheduleAuthorizer.default(:updatable, user)`
194 ScheduleAuthorizer.updatable_by?(user)
195 ```
1b4be57 @nathanl First pass at docs
authored
196
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
197 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.
e1ab32e @nathanl Massive README reorg
authored
198
dad9ccf @nathanl Updated README to show options
authored
199 <a name="passing_options">
200 #### Passing Options
201
202 Any options you pass when checking permissions will be passed right up the chain. One use case for this would be if you needed an associated instance in order to do a class-level check. For example:
203
204 ```ruby
205 # I don't have a comment instance to check, but I need to know
206 # which post the user wants to comment on
207 user.can_create?(Comment, :for => @post)
208 ```
209
210 This would ultimately call `creatable_by?` on the designated authorizer with two arguments: the user and `{:for => @post}`. If you've defined that method yourself, you'd need to ensure that it accepts the options hash before doing this, or you'd get a "wrong number of arguments" error.
211
212 There's nothing special about the hash key `:for`; I just think it reads well in this case. You can pass any options that make sense in your case.
213
214 If you **don't** pass options, none will be passed to your authorizer, either.
215
216 And you could always handle the case above without options if you don't mind creating an extra model instance:
217
218 ```ruby
219 user.can_create?(Comment.new(:post => @post))
220 ```
221
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
222 <a name="default_methods">
223 #### Default Methods
f0c47ce @nathanl README now discusses methods
authored
224
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
225 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.
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
226
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
227 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:
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
228
1deedd4 @nathanl Will Github highlight this nicely?
authored
229 ```ruby
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
230 # app/authorizers/application_authorizer.rb
231 class ApplicationAuthorizer < Authority::Authorizer
232
233 # Example call: `default(:creatable, current_user)`
234 def self.default(able, user)
235 has_role_granting?(user, able) || user.admin?
236 end
237
238 protected
239
240 def has_role_granting(user, able)
241 # Does the user have any of the roles which give this permission?
242 (roles_which_grant(able) & user.roles).any?
243 end
244
245 def roles_which_grant(able)
246 # Look up roles for the current authorizer and `able`
247 ...
248 end
6ece493 @nathanl Do..end looks nicer here
authored
249 end
1deedd4 @nathanl Will Github highlight this nicely?
authored
250 ```
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
251
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
252 If your system is uniform enough, **this method alone might handle all the logic you need**.
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
253
c6c4e47 @nathanl Describe testing authorizers
authored
254 <a name="testing_authorizers">
255 #### Testing Authorizers
256
257 One nice thing about putting your authorization logic in authorizers is the ease of testing. Here's a brief example.
258
1deedd4 @nathanl Will Github highlight this nicely?
authored
259 ```ruby
3ce2f2f @nathanl use Github-flavored markdown
authored
260 # An authorizer shared by several admin-only models
261 describe AdminAuthorizer do
c6c4e47 @nathanl Describe testing authorizers
authored
262
3ce2f2f @nathanl use Github-flavored markdown
authored
263 before :each do
264 @user = FactoryGirl.build(:user)
265 @admin = FactoryGirl.build(:admin)
266 end
c6c4e47 @nathanl Describe testing authorizers
authored
267
3ce2f2f @nathanl use Github-flavored markdown
authored
268 describe "class" do
269 it "should let admins update in bulk" do
270 AdminAuthorizer.should be_bulk_updatable_by(@admin)
271 end
c6c4e47 @nathanl Describe testing authorizers
authored
272
3ce2f2f @nathanl use Github-flavored markdown
authored
273 it "should not let users update in bulk" do
274 AdminAuthorizer.should_not be_bulk_updatable_by(@user)
275 end
276 end
c6c4e47 @nathanl Describe testing authorizers
authored
277
3ce2f2f @nathanl use Github-flavored markdown
authored
278 describe "instances" do
c6c4e47 @nathanl Describe testing authorizers
authored
279
3ce2f2f @nathanl use Github-flavored markdown
authored
280 before :each do
281 # A mock model that uses AdminAuthorizer
282 @admin_resource_instance = mock_admin_resource
283 end
c6c4e47 @nathanl Describe testing authorizers
authored
284
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
285 it "should let admins delete" do
286 @admin_resource_instance.authorizer.should be_deletable_by(@admin)
3ce2f2f @nathanl use Github-flavored markdown
authored
287 end
c6c4e47 @nathanl Describe testing authorizers
authored
288
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
289 it "should not let users delete" do
290 @admin_resource_instance.authorizer.should_not be_deletable_by(@user)
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
291 end
3ce2f2f @nathanl use Github-flavored markdown
authored
292
293 end
294
295 end
1deedd4 @nathanl Will Github highlight this nicely?
authored
296 ```
85fca47 @nathanl Diagrams! Everybody loves 'em.
authored
297
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
298 <a name="controllers">
adac9d2 @nathanl Improvements to README
authored
299 ### Controllers
300
c8f1b1a @nathanl Rename SecurityTransgression to SecurityViolation
authored
301 Anytime a controller finds a user attempting something they're not authorized to do, a [Security Violation](#security_violations_and_logging) will result. Controllers get two ways to check authorization:
adac9d2 @nathanl Improvements to README
authored
302
8c6dd66 @nathanl README tweaks for clarity (thanks, @michaeltherobot)
authored
303 - `authorize_actions_for Llama` protects multiple controller actions with a `before_filter`, which performs a **class-level** check. If the current user is never allowed to delete a `Llama`, they'll never even get to the controller's `destroy` method.
304 - `authorize_action_for @llama` can be called inside a single controller action, and performs an **instance-level** check. If called inside `update`, it will check whether the current user is allowed to update this particular `@llama` instance.
adac9d2 @nathanl Improvements to README
authored
305
8c6dd66 @nathanl README tweaks for clarity (thanks, @michaeltherobot)
authored
306 How does Authority know to check `deletable_by?` before the controller's `destroy` action? It checks your configuration. These mappings are configurable globally from the initializer file. Defaults are as follows:
307
308 ```ruby
309 config.controller_action_map = {
310 :index => 'read', # `index` controller action will check `readable_by?`
311 :show => 'read',
312 :new => 'create', # `new` controller action will check `creatable_by?`
313 :create => 'create', # ...etc
314 :edit => 'update',
315 :update => 'update',
316 :destroy => 'delete'
317 }
318 ```
319
320 They are also configurable per controller, as follows:
adac9d2 @nathanl Improvements to README
authored
321
1deedd4 @nathanl Will Github highlight this nicely?
authored
322 ```ruby
640ab93 @nathanl Controller name should be plural
authored
323 class LlamasController < ApplicationController
a86a6d6 @nathanl Improved documentation for controller methods
authored
324
3ce2f2f @nathanl use Github-flavored markdown
authored
325 # Check class-level authorizations before all actions except :create
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
326 # Also, to authorize this controller's 'neuter' action, ask whether `current_user.can_update?(Llama)`
327 authorize_actions_for Llama, :except => :create, :actions => {:neuter => :update},
3ce2f2f @nathanl use Github-flavored markdown
authored
328
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
329 # To authorize this controller's 'breed' action, ask whether `current_user.can_create?(Llama)`
8c6dd66 @nathanl README tweaks for clarity (thanks, @michaeltherobot)
authored
330 authority_action :breed => 'create'
a86a6d6 @nathanl Improved documentation for controller methods
authored
331
3ce2f2f @nathanl use Github-flavored markdown
authored
332 ...
a86a6d6 @nathanl Improved documentation for controller methods
authored
333
3ce2f2f @nathanl use Github-flavored markdown
authored
334 def edit
335 @llama = Llama.find(params[:id])
53609fc @scottmartin Update README.markdown
scottmartin authored
336 authorize_action_for(@llama) # Check to see if you're allowed to edit this llama. failure == SecurityViolation
337 end
338
339 def update
340 @llama = Llama.find(params[:id])
341 authorize_action_for(@llama) # Check to see if you're allowed to edit this llama.
3ce2f2f @nathanl use Github-flavored markdown
authored
342 @llama.attributes = params[:llama] # Don't save the attributes before authorizing
53609fc @scottmartin Update README.markdown
scottmartin authored
343 authorize_action_for(@llama) # Check again, to see if the changes are allowed.
3ce2f2f @nathanl use Github-flavored markdown
authored
344 if @llama.save?
345 # etc
346 end
a86a6d6 @nathanl Improved documentation for controller methods
authored
347
3ce2f2f @nathanl use Github-flavored markdown
authored
348 end
1deedd4 @nathanl Will Github highlight this nicely?
authored
349 ```
15bfa3c @nathanl More README tweaking
authored
350
355efe3 @nathanl Document passing options to
authored
351 As with other authorization checks, you can also pass options here, and they'll be sent along to your authorization method: `authorize_action_for(@llama, :sporting => @hat_style)`. Generally, though, your authorization will depend on some attribute or association of the model instance, so the authorizer can check `@llama.neck_strength` and `@llama.owner.nationality`, etc, without needing any additional information.
352
e1ab32e @nathanl Massive README reorg
authored
353 <a name="views">
354 ### Views
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
355
356 Assuming your user object is available in your views, you can do all kinds of conditional rendering. For example:
357
1deedd4 @nathanl Will Github highlight this nicely?
authored
358 ```ruby
3ce2f2f @nathanl use Github-flavored markdown
authored
359 link_to 'Edit Widget', edit_widget_path(@widget) if current_user.can_update?(@widget)
1deedd4 @nathanl Will Github highlight this nicely?
authored
360 ```
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
361
c8f1b1a @nathanl Rename SecurityTransgression to SecurityViolation
authored
362 If the user isn't allowed to edit widgets, they won't see the link. If they're nosy and try to hit the URL directly, they'll get a [Security Violation](#security_violations_and_logging) from the controller.
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
363
e1ab32e @nathanl Massive README reorg
authored
364 <a name="security_violations_and_logging">
365 ## Security Violations & Logging
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
366
69ad496 @nathanl Can now specify custom security violation handler
authored
367 Anytime a user attempts an unauthorized action, Authority calls whatever controller method is specified by your `security_violation_handler` option, handing it the exception. The default handler is `authority_forbidden`, which Authority adds to your `ApplicationController`. It does the following:
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
368
69ad496 @nathanl Can now specify custom security violation handler
authored
369 - Renders `public/403.html`
c263bd3 @nathanl More README improvements. IT SHALL BE PERFECT, OH YES.
authored
370 - Logs the violation to whatever logger you configured.
371
2f60ba3 @nathanl Suggested alternate handler for violations [ci skip]
authored
372 You can define your own `authority_forbidden` method:
373
374
375 ```ruby
376 # Send 'em back where they came from with a slap on the wrist
377 def authority_forbidden(exception)
c0f2c62 @nathanl Show logging messages on forbidden actions
authored
378 Authority.configuration.logger.warn(error.message)
2f60ba3 @nathanl Suggested alternate handler for violations [ci skip]
authored
379 redirect_to request.referrer.presence || root_path, :alert => 'You are not authorized to complete that action.'
380 end
381 ```
382
383 ... or specify a different handler like this:
15bfa3c @nathanl More README tweaking
authored
384
69ad496 @nathanl Can now specify custom security violation handler
authored
385 ```ruby
386 # config/initializers/authority.rb
387 config.security_violation_handler = :fire_ze_missiles
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
388 ```
389 Then define the method on your controller:
69ad496 @nathanl Can now specify custom security violation handler
authored
390
4c2246e @nathanl Squashed commit: models assume ApplicationAuthorizer;
authored
391 ```ruby
69ad496 @nathanl Can now specify custom security violation handler
authored
392 # app/controllers/application_controller.rb
393 class ApplicationController < ActionController::Base
394
395 def fire_ze_missiles(exception)
fff9aee @nathanl comment formatting
authored
396 # Log? Set a flash message? Dispatch minions to
397 # fill their mailbox with goose droppings? It's up to you.
69ad496 @nathanl Can now specify custom security violation handler
authored
398 end
399 ...
400 end
401 ```
6623df9 @nathanl Update README
authored
402
69ad496 @nathanl Can now specify custom security violation handler
authored
403 If you want different error handling per controller, define `fire_ze_missiles` on each of them.
6623df9 @nathanl Update README
authored
404
2720c8e @nathanl Mentioned methods on `SecurityViolation`
authored
405 Your method will be handed the `SecurityViolation`, which has a `message` method. In case you want to build your own message, it also exposes `user`, `action` and `resource`.
406
e1ab32e @nathanl Massive README reorg
authored
407 <a name="credits">
15bfa3c @nathanl More README tweaking
authored
408 ## Credits, AKA 'Shout-Outs'
1b4be57 @nathanl First pass at docs
authored
409
7dbb8b2 @nathanl Revised shout-outs
authored
410 - [adamhunter](https://github.com/adamhunter) for pairing with me on this gem. The only thing faster than his typing is his brain.
411 - [nkallen](https://github.com/nkallen) for writing [a lovely blog post on access control](http://pivotallabs.com/users/nick/blog/articles/272-access-control-permissions-in-rails) when he worked at Pivotal Labs. I cried sweet tears of joy when I read that a couple of years ago. I was like, "Zee access code, she is so BEEUTY-FUL!"
412 - [jnunemaker](https://github.com/jnunemaker) for later creating [Canable](http://github.com/jnunemaker/canable), another inspiration for Authority.
413 - [TMA](http://www.tma1.com) for employing me and letting me open source some of our code.
1b4be57 @nathanl First pass at docs
authored
414
e1ab32e @nathanl Massive README reorg
authored
415 <a name="contributing">
6484594 @nathanl Updated TODO and README
authored
416
417 ## Responses, AKA 'Hollaback'
418
419 Do you like Authority? Has it cleaned up your code, made you more personable, and taught you the Secret to True Happiness? Awesome! I'd **love** to get email from you - see my Github profile for the address.
420
7e96a6b @nathanl initial commit
authored
421 ## Contributing
422
6484594 @nathanl Updated TODO and README
authored
423 How can you contribute? Let me count the ways.
424
425 ### 1. Publicity
426
427 If you like Authority, tell people! Blog, tweet, comment, or even... [shudder]... talk with people *in person*. If you feel up to it, I mean. It's OK if you don't.
428
429 ### 2. Documentation
430
5bd179a @nathanl README fixes.
authored
431 Add examples to the [wiki](http://github.com/nathanl/authority/wiki) to help others solve problems like yours.
6484594 @nathanl Updated TODO and README
authored
432
433 ### 3. Issues
434
435 Tell me your problems and/or ideas.
436
437 ### 4. Code or documentation
2dabdeb @nathanl Move TODOs to separate file
authored
438
5bd179a @nathanl README fixes.
authored
439 1. Have an idea. If you don't have one, check the TODO file or grep the project for 'TODO' comments.
440 2. Open an issue so we can talk it over.
441 3. Fork this project
442 4. Create your feature branch (`git checkout -b my-new-feature`)
443 5. `bundle install` to get all dependencies
444 6. `rspec spec` to run all tests.
445 7. Update/add tests for your changes and code until they pass.
446 8. Commit your changes (`git commit -am 'Added some feature'`)
447 9. Push to the branch (`git push origin my-new-feature`)
448 10. Create a new Pull Request
Something went wrong with that request. Please try again.