rurounijones-acl9 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ * Fix namespaced models in roles backend (thanks goes to Tomas Jogin)
2
+ * Action name override in boolean methods.
3
+ * @:query_method@ option for @access_control@.
4
+
5
+ h2. 0.10.0 (03-May-2009)
6
+
7
+ * Use context+matchy combo for testing
8
+ * Bugfix: unwanted double quote in generated SQL statement
9
+
10
+ h2. 0.9.4 (27-Feb-2009)
11
+
12
+ * Introduce :if and :unless rule options.
13
+
14
+ h2. 0.9.3 (04-Feb-2009)
15
+
16
+ * Fix bug in delete_role - didn't work with custom class names
17
+ * Add @:helper@ option for @access_control@.
18
+ * Ability to generate helper methods directly (place @include Acl9Helpers@ in your helper module).
19
+
20
+ h2. 0.9.2 (11-Jan-2009)
21
+
22
+ * @access_control :method do end@ as shorter form for @access_control :as_method => :method do end@.
23
+ * Boolean method can now receive an objects hash which will be looked into first before taking
24
+ an instance variable.
25
+
26
+ h2. 0.9.1 (03-Jan-2009)
27
+
28
+ Initial release.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Oleg Dashevskii
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,891 @@
1
+ h1. Acl9
2
+
3
+ Acl9 is yet another solution for role-based authorization in Rails. It consists of two
4
+ subsystems which can be used separately.
5
+
6
+ *Role control subsystem* allows you to set and query user roles for various objects.
7
+
8
+ *Access control subsystem* allows you to specify different role-based access rules
9
+ inside controllers.
10
+
11
+ A bunch of access rules is translated into a complex
12
+ boolean expression. Then it's turned into a lambda or a method and can
13
+ be used with @before_filter@. Thus you can block unprivileged access to certain
14
+ actions of your controller.
15
+
16
+ An example:
17
+
18
+ <pre><code>
19
+ class VerySecretController < ApplicationController
20
+ access_control do
21
+ allow :superadmin
22
+ allow :owner, :of => :secret
23
+
24
+ action :index do
25
+ allow anonymous, logged_in
26
+ end
27
+
28
+ allow logged_in, :to => :show
29
+ allow :manager, :of => :secret, :except => [:delete, :destroy]
30
+ deny :thiefs
31
+ end
32
+
33
+ def index
34
+ # ...
35
+ end
36
+
37
+ # ...
38
+ end
39
+ </code></pre>
40
+
41
+ h1. Changes
42
+
43
+ This version of Acl9 by rurounijones has been modified from the original (be9's version).
44
+
45
+ The main changes are to remove the HABTM relationship between the subject and the role. Now
46
+ the relationship is a has_many, this is to allow for has_many :through relationships to be
47
+ setup.
48
+
49
+ This means that the number of records in the role table will be much higher than the original.
50
+
51
+ Apart from that there are no other changes and the API remains the same.
52
+
53
+ h1. Contacts
54
+
55
+ The original Acl9 is hosted "on the GitHub":http://github.com/be9/acl9.
56
+
57
+ This version of Acl9 is also hosted "on the Github":http://github.com/rurounijones/acl9.
58
+
59
+ You may find tutorials and additional docs on the "wiki page":http://wiki.github.com/be9/acl9.
60
+
61
+ Rdocs are available "here":http://rdoc.info/projects/be9/acl9.
62
+
63
+ If you have questions regarding the original Acl9, please post to the
64
+ "acl9-discuss group":http://groups.google.com/group/acl9-discuss
65
+
66
+ h1. Installation
67
+
68
+ This version of Acl9 can be installed as a gem from "GitHub":http://github.com.
69
+
70
+ Add the following line to your @config/environment.rb@:
71
+
72
+ <pre><code>
73
+ config.gem "rurounijones-acl9", :source => "http://gems.github.com", :lib => "acl9"
74
+ </pre></code>
75
+
76
+ Then run @rake gems:install@ (with possible @rake gems:unpack@ thereafter) and you're done!
77
+
78
+ Alternatively you can install Acl9 as a plugin:
79
+
80
+ <pre><code>
81
+ script/plugin install git://github.com/be9/acl9.git
82
+ </pre></code>
83
+
84
+ h1. Basics
85
+
86
+ h2. Authorization is not authentication!
87
+
88
+ Both words start with "auth" but have different meaning!
89
+
90
+ *Authentication* is basically a mapping of credentials (username, password) or
91
+ OpenID to specific user account in the system.
92
+
93
+ *Authorization* is an _authenticated_ user's permission to perform some
94
+ specific action somewhere in the system.
95
+
96
+ Acl9 is a authorization solution, so you will need to implement authentication
97
+ by other means. I recommend "Authlogic":http://github.com/binarylogic/authlogic
98
+ for that purpose, as it's simple, clean and at the same time very configurable.
99
+
100
+ h2. Roles
101
+
102
+ Role is an abstraction. You could directly assign permissions to user accounts in
103
+ your system, but you'd not want to! Way more manageable solution is to assign permissions
104
+ to roles and roles further to users.
105
+
106
+ For example, you can have role called _admin_ which has all available permissions. Now
107
+ you may assign this role to several trusted accounts on your system.
108
+
109
+ Acl9 also supports the notion of _object roles_, that is, roles with limited scope.
110
+
111
+ Imagine we are building a magazine site and want to develop a permission system. So, what roles
112
+ and permissions are there?
113
+
114
+ _Journalists_ should be able to create articles in their section and edit their own articles.
115
+
116
+ _Section editors_ should be able to edit and delete all articles in their sections and
117
+ change the _published_ flag.
118
+
119
+ _Editor-in-chief_ should be able to change everything.
120
+
121
+ We clearly see that journalists and section editors are tied to a specific section, whereas
122
+ editor-in-chief is a role with global scope.
123
+
124
+ h2. Role interface
125
+
126
+ All permission checks in Acl9 are boiled down to calls of a single method:
127
+
128
+ @subject.has_role?(role, object)@
129
+
130
+ That should be read as "Does _subject_ have _role_ on _object_?".
131
+
132
+ Subject is an instance of a @User@, or @Account@, or whatever model you use for
133
+ authentication. Object is an instance of any class (including subject class!)
134
+ or @nil@ (in which case it's a global role).
135
+
136
+ Acl9 builtin role control subsystem provides @has_role?@ method for you, but you can
137
+ also implemented it by hand (see _Coming up with your own role implementation_ below).
138
+
139
+ h1. Acl9 role control subsystem
140
+
141
+ Role control subsystem has been lifted from
142
+ "Rails authorization plugin":http://github.com/DocSavage/rails-authorization-plugin,
143
+ but undergone some modifications.
144
+
145
+ It's based on two tables in the database. First, role table, which stores pairs @[role_name, object]@
146
+ where object is a polymorphic model instance or a class. Second, join table, which joins users and roles.
147
+
148
+ To use this subsystem, you should define a @Role@ model.
149
+
150
+ h2. Role model
151
+
152
+ <pre><code>
153
+ class Role < ActiveRecord::Base
154
+ acts_as_authorization_role
155
+ end
156
+ </code></pre>
157
+
158
+ The structure of @roles@ table is as follows:
159
+
160
+ <pre><code>
161
+ create_table "roles", :force => true do |t|
162
+ t.integer "user_id", # Change if your subject is not user
163
+ t.string "name", :limit => 40
164
+ t.string "authorizable_type", :limit => 40
165
+ t.integer "authorizable_id"
166
+ t.datetime "created_at"
167
+ t.datetime "updated_at"
168
+ end
169
+ </code></pre>
170
+
171
+ Note that you will almost never use the @Role@ class directly.
172
+
173
+ h2. Subject model
174
+
175
+ <pre><code>
176
+ class User < ActiveRecord::Base
177
+ acts_as_authorization_subject
178
+ end
179
+ </code></pre>
180
+
181
+ h2. Object model
182
+
183
+ Place @acts_as_authorization_object@ call inside any model you want to act
184
+ as such.
185
+
186
+ <pre><code>
187
+ class Foo < ActiveRecord::Base
188
+ acts_as_authorization_object
189
+ end
190
+
191
+ class Bar < ActiveRecord::Base
192
+ acts_as_authorization_object
193
+ end
194
+ </code></pre>
195
+
196
+ h2. Interface
197
+
198
+ h3. Subject model
199
+
200
+ A call of @acts_as_authorization_subject@ defines following methods on the model:
201
+
202
+ @subject.has_role?(role, object = nil)@. Returns @true@ of @false@ (has or has not).
203
+
204
+ @subject.has_role!(role, object = nil)@. Assigns a @role@ for the @object@ to the @subject@.
205
+ Does nothing is subject already has such a role.
206
+
207
+ @subject.has_no_role!(role, object = nil)@. Unassigns a role from the @subject@.
208
+
209
+ @subject.has_roles_for?(object)@. Does the @subject@ has any roles for @object@? (@true@ of @false@)
210
+
211
+ @subject.has_role_for?(object)@. Same as @has_roles_for?@.
212
+
213
+ @subject.roles_for(object)@. Returns an array of @Role@ instances, corresponding to @subject@ 's roles on
214
+ @object@. E.g. @subject.roles_for(object).map(&:name).sort@ will give you role names in alphabetical order.
215
+
216
+ @subject.has_no_roles_for!(object)@. Unassign any @subject@ 's roles for a given @object@.
217
+
218
+ @subject.has_no_roles!@. Unassign all roles from @subject@.
219
+
220
+ h3. Object model
221
+
222
+ A call of @acts_as_authorization_object@ defines following methods on the model:
223
+
224
+ @object.accepts_role?(role_name, subject)@. An alias for @subject.has_role?(role_name, object)@.
225
+
226
+ @object.accepts_role!(role_name, subject)@. An alias for @subject.has_role!(role_name, object)@.
227
+
228
+ @object.accepts_no_role!(role_name, subject)@. An alias for @subject.has_no_role!(role_name, object)@.
229
+
230
+ @object.accepts_roles_by?(subject)@. An alias for @subject.has_roles_for?(object)@.
231
+
232
+ @object.accepts_role_by?(subject)@. Same as @accepts_roles_by?@.
233
+
234
+ @object.accepts_roles_by(subject)@. An alias for @subject.roles_for(object)@.
235
+
236
+ h2. Custom class names
237
+
238
+ You may want to deviate from default @User@ and @Role@ class names. That can easily be done with
239
+ arguments to @acts_as_...@.
240
+
241
+ Say, you have @Account@ and @AccountRole@:
242
+
243
+ <pre><code>
244
+ class Account < ActiveRecord::Base
245
+ acts_as_authorization_subject :role_class_name => 'AccountRole'
246
+ end
247
+
248
+ class AccountRole < ActiveRecord::Base
249
+ acts_as_authorization_role :subject_class_name => 'Account'
250
+ end
251
+
252
+ class FooBar < ActiveRecord::Base
253
+ acts_as_authorization_object :role_class_name => 'AccountRole', :subject_class_name => 'Account'
254
+ end
255
+ </code></pre>
256
+
257
+ Or... since Acl9 defaults can be changed in a special hash, you can put the following snippet:
258
+
259
+ <pre><code>
260
+ Acl9::config.merge!({
261
+ :default_role_class_name => 'AccountRole',
262
+ :default_subject_class_name => 'Account',
263
+ })
264
+ </code></pre>
265
+
266
+ ... into @config/initializers/acl9.rb@ and get rid of that clunky arguments:
267
+
268
+ <pre><code>
269
+ class Account < ActiveRecord::Base
270
+ acts_as_authorization_subject
271
+ end
272
+
273
+ class AccountRole < ActiveRecord::Base
274
+ acts_as_authorization_role
275
+ end
276
+
277
+ class FooBar < ActiveRecord::Base
278
+ acts_as_authorization_object
279
+ end
280
+ </code></pre>
281
+
282
+ Note that you'll need to change your database structure appropriately:
283
+
284
+ <pre><code>
285
+ create_table "account_roles", :force => true do |t|
286
+ t.string "name", :limit => 40
287
+ t.string "authorizable_type", :limit => 40
288
+ t.integer "authorizable_id"
289
+ t.datetime "created_at"
290
+ t.datetime "updated_at"
291
+ end
292
+
293
+ create_table "account_roles_accounts", :id => false, :force => true do |t|
294
+ t.integer "account_id"
295
+ t.integer "account_role_id"
296
+ t.datetime "created_at"
297
+ t.datetime "updated_at"
298
+ end
299
+ </code></pre>
300
+
301
+ h2. Examples
302
+
303
+ <pre><code>
304
+ user = User.create!
305
+ user.has_role? 'admin' # => false
306
+
307
+ user.has_role! :admin
308
+
309
+ user.has_role? :admin # => true
310
+ </code></pre>
311
+
312
+ @user@ now has global role _admin_. Note that you can specify role name either
313
+ as a string or as a symbol.
314
+
315
+ <pre><code>
316
+ foo = Foo.create!
317
+
318
+ user.has_role? 'admin', foo # => false
319
+
320
+ user.has_role! :manager, foo
321
+
322
+ user.has_role? :manager, foo # => true
323
+ foo.accepts_role? :manager, user # => true
324
+
325
+ user.has_roles_for? foo # => true
326
+ </code></pre>
327
+
328
+ You can see here that global and object roles are distinguished from each other. User
329
+ with global role _admin_ isn't automatically admin of @foo@.
330
+
331
+ However,
332
+
333
+ <pre><code>
334
+ user.has_role? :manager # => true
335
+ </code></pre>
336
+
337
+ That is, if you have an object role, it means that you have a global role with the same name too!
338
+ In other words, you are _manager_ if you manage at least one @foo@ (or a @bar@...).
339
+
340
+ <pre><code>
341
+ bar = Bar.create!
342
+
343
+ user.has_role! :manager, bar
344
+ user.has_no_role! :manager, foo
345
+
346
+ user.has_role? :manager, foo # => false
347
+ user.has_role? :manager # => true
348
+ </code></pre>
349
+
350
+ Our @user@ is no more manager of @foo@, but has become a manager of @bar@.
351
+
352
+ <pre><code>
353
+ user.has_no_roles!
354
+
355
+ user.has_role? :manager # => false
356
+ user.has_role? :admin # => false
357
+ user.roles # => []
358
+ </code></pre>
359
+
360
+ At this time @user@ has no roles in the system.
361
+
362
+ h2. Coming up with your own role implementation
363
+
364
+ The described role system with its 2 tables (not counting the @users@ table!)
365
+ might be an overkill for many cases. If all you want is global roles without
366
+ any scope, you'd better off implementing it by hand.
367
+
368
+ The access control subsystem of Acl9 uses only @subject.has_role?@ method, so
369
+ there's no need to implement anything else except for own convenience.
370
+
371
+ For example, if each your user can have only one global role, just add @role@
372
+ column to your @User@ class:
373
+
374
+ <pre><code>
375
+ class User < ActiveRecord::Base
376
+ def has_role?(role_name, obj=nil)
377
+ self.role == role_name
378
+ end
379
+
380
+ def has_role!(role_name, obj=nil)
381
+ self.role = role_name
382
+ save!
383
+ end
384
+ end
385
+ </code></pre>
386
+
387
+ If you need to assign multiple roles to your users, you can use @serialize@
388
+ with role array or a special solution like
389
+ "preference_fu":http://github.com/brennandunn/preference_fu.
390
+
391
+ h1. Access control subsystem
392
+
393
+ By means of access control subsystem you can protect actions of your controller
394
+ from unauthorized access. Acl9 provides a nice DSL for writing access rules.
395
+
396
+ h2. Allow and deny
397
+
398
+ Access control is mostly about allowing and denying. So there are two
399
+ basic methods: @allow@ and @deny@. They have the same syntax:
400
+
401
+ <pre><code>
402
+ allow ROLE_LIST, OPTIONS
403
+ deny ROLE_LIST, OPTIONS
404
+ </code></pre>
405
+
406
+ h3. Specifying roles
407
+
408
+ ROLE_LIST is a list of roles (at least 1 role should be there). So,
409
+
410
+ <pre><code>
411
+ allow :manager, :admin
412
+ deny :banned
413
+ </code></pre>
414
+ will match holders of global role _manager_ *and* holders of global role _admin_ as allowed.
415
+ On the contrary, holders of _banned_ role will match as denied.
416
+
417
+ Basically this snippet is equivalent to
418
+
419
+ <pre><code>
420
+ allow :manager
421
+ allow :admin
422
+ deny :banned
423
+ </code></pre>
424
+ which means that roles in argument list are OR'ed for a match, and not AND'ed.
425
+
426
+ Also note that:
427
+ * You may use both strings and :symbols to specify roles (the latter get converted into strings).
428
+ * Role names are singularized before check.
429
+
430
+ Thus the snippet above can also be written as
431
+
432
+ <pre><code>
433
+ allow :managers, :admins
434
+ deny 'banned'
435
+ </code></pre>
436
+ or even
437
+
438
+ <pre><code>
439
+ allow *%w(managers admins)
440
+ deny 'banned'
441
+ </code></pre>
442
+
443
+ h3. Object and class roles
444
+
445
+ Examples in the previous section were all about global roles. Let's see how we can
446
+ use object and class roles in the ACL block.
447
+
448
+ <pre><code>
449
+ allow :responsible, :for => Widget
450
+ allow :possessor, :of => :foo
451
+ deny :angry, :at => :me
452
+ allow :interested, :in => Future
453
+ deny :short, :on => :time
454
+ deny :hated, :by => :us
455
+ </code></pre>
456
+
457
+ To specify an object you use one of the 6 preposition options:
458
+ * :of
459
+ * :at
460
+ * :on
461
+ * :by
462
+ * :for
463
+ * :in
464
+
465
+ They all have the same meaning, use one that makes better English out of your rule.
466
+
467
+ Now, each of these prepositions may point to a Class or a :symbol. In the former case we get
468
+ a class role. E.g. @allow :responsible, :for => Widget@ becomes @subject.has_role?('responsible', Widget)@.
469
+
470
+ Symbol is trickier, it means that the appropriate instance variable of the controller is taken
471
+ as an object.
472
+
473
+ @allow :possessor, :of => :foo@ is translated into
474
+ <code>subject.has_role?('possessor', controller.instance_variable_get('@foo'))</code>.
475
+
476
+ Checking against an instance variable has sense when you have another _before filter_ which is executed
477
+ *before* the one generated by @access_control@, like this:
478
+
479
+ <pre><code>
480
+ class MoorblesController < ApplicationController
481
+ before_filter :load_moorble, :only => [:edit, :update, :destroy]
482
+
483
+ access_control do
484
+ allow :creator, :of => :moorble
485
+
486
+ # ...
487
+ end
488
+
489
+ # ...
490
+
491
+ private
492
+
493
+ def load_moorble
494
+ @moorble = Moorble.find(params[:id])
495
+ end
496
+ end
497
+ </code></pre>
498
+
499
+ Note that the object option is applied to all of the roles you specify in the argument list.
500
+ As such,
501
+
502
+ <pre><code>
503
+ allow :devil, :son, :of => God
504
+ </code></pre>
505
+ is equivalent to
506
+
507
+ <pre><code>
508
+ allow :devil, :of => God
509
+ allow :son, :of => God
510
+ </code></pre>
511
+
512
+ but *NOT*
513
+
514
+ <pre><code>
515
+ allow :devil
516
+ allow :son, :of => God
517
+ </code></pre>
518
+
519
+ h3. Pseudo-roles
520
+
521
+ There are three pseudo-roles in the ACL: @all@, @anonymous@ and @logged_in@.
522
+
523
+ @allow all@ will always match (as well as @deny all@).
524
+
525
+ @allow anonymous@ and @deny anonymous@ will match when user is anonymous, i.e. subject is @nil@.
526
+ You may also use a shorter notation: @allow nil@ (@deny nil@).
527
+
528
+ @logged_in@ is direct opposite of @anonymous@, so @allow logged_in@ will match if the user is logged in
529
+ (subject is not @nil@).
530
+
531
+ No role checks are done in either case.
532
+
533
+ h3. Limiting action scope
534
+
535
+ By default rules apply to all actions of the controller. There are two options that
536
+ narrow the scope of the @deny@ or @allow@ rule: @:to@ and @:except@.
537
+
538
+ <pre><code>
539
+ allow :owner, :of => :site, :to => [:delete, :destroy]
540
+ deny anonymous, :except => [:index, :show]
541
+ </code></pre>
542
+
543
+ For the first rule to match not only the current user should be an _owner_ of the _site_, but also
544
+ current action should be _delete_ or _destroy_.
545
+
546
+ In the second rule anonymous user access is denied for all actions, except _index_ and _show_.
547
+
548
+ You may not specify both @:to@ and @:except@.
549
+
550
+ Note that you can use actions block instead of @:to@ (see _Actions block_
551
+ below). You can also use @:only@ and @:except@ options in the
552
+ @access_control@ call which will serve as options of the @before_filter@ and thus
553
+ limit the scope of the whole ACL.
554
+
555
+ h3. Rule conditions
556
+
557
+ You may create conditional rules using @:if@ and @:unless@ options.
558
+
559
+ <pre><code>
560
+ allow :owner, :of => :site, :to => [:delete, :destroy], :if => :chance_to_delete
561
+ </code></pre>
562
+
563
+ Controller's @:chance_to_delete@ method will be called here. The rule will match if the action
564
+ is 'delete' or 'destroy' AND if the method returned @true@.
565
+
566
+ @:unless@ has the opposite meaning and should return @false@ for a rule to match.
567
+
568
+ Both options can be specified in the same rule.
569
+
570
+ <pre><code>
571
+ allow :visitor, :to => [:index, :show], :if => :right_phase_of_the_moon?, :unless => :suspicious?
572
+ </code></pre>
573
+
574
+ @right_phase_of_the_moon?@ should return @true@ AND @suspicious?@ should return @false@ for a poor visitor to
575
+ see a page.
576
+
577
+ Currently only controller methods are supported (specify them as :symbols). Lambdas are *not* supported.
578
+
579
+ h2. Rule matching order
580
+
581
+ Rule matching system is similar to that of Apache web server. There are two modes: _default allow_
582
+ (corresponding to @Order Deny,Allow@ in Apache) and _default deny_ (@Order Allow,Deny@ in Apache).
583
+
584
+ h3. Setting modes
585
+
586
+ Mode is set with a @default@ call.
587
+
588
+ @default :allow@ will set _default allow_ mode.
589
+
590
+ @default :deny@ will set _default deny_ mode. Note that this is the default mode, i.e. it will be on
591
+ if you don't do a @default@ call at all.
592
+
593
+ h3. Matching algorithm
594
+
595
+ First of all, regardless of the mode, all @allow@ matches are OR'ed together and all @deny@ matches
596
+ are OR'ed as well.
597
+
598
+ We'll express this in the following manner:
599
+
600
+ <pre><code>
601
+ ALLOWED = (allow rule 1 matches?) OR ((allow rule 2 matches?) OR ...
602
+ NOT_DENIED = NOT ((deny rule 1 matches?) OR (deny rule 2 matches?) OR ...)
603
+ </code></pre>
604
+
605
+ So, ALLOWED is @true@ when either of the @allow@ rules matches, and NOT_DENIED is @true@ when none
606
+ of the @deny@ rules matches.
607
+
608
+ Let's denote the final result of algorithm as ALLOWANCE. If it's @true@, access is allowed, if @false@ - denied.
609
+
610
+ In the case of _default allow_:
611
+ <pre><code>
612
+ ALLOWANCE = ALLOWED OR NOT_DENIED
613
+ </code></pre>
614
+
615
+ In the case of _default deny_:
616
+ <pre><code>
617
+ ALLOWANCE = ALLOWED AND NOT_DENIED
618
+ </code></pre>
619
+
620
+ Same result as a table:
621
+
622
+ |_. Rule matches |_. Default allow mode |_. Default deny mode |
623
+ | None of the @allow@ and @deny@ rules matched. | Access is allowed. | Access is denied. |
624
+ | Some of the @allow@ rules matched, none of the @deny@ rules matched. | Access is allowed. | Access is allowed. |
625
+ | None of the @allow@ rules matched, some of the @deny@ rules matched. | Access is denied. | Access is denied. |
626
+ | Some of the @allow@ rules matched, some of the @deny@ rules matched. | Access is allowed. | Access is denied. |
627
+
628
+ Apparently _default deny_ mode is more strict, and that's because it's on by default.
629
+
630
+ h2. Actions block
631
+
632
+ You may group rules with the help of the @actions@ block.
633
+
634
+ An example from the imaginary @PostsController@:
635
+
636
+ <pre><code>
637
+ allow :admin
638
+
639
+ actions :index, :show do
640
+ allow all
641
+ end
642
+
643
+ actions :new, :create do
644
+ allow :managers, :of => Post
645
+ end
646
+
647
+ actions :edit, :update do
648
+ allow :owner, :of => :post
649
+ end
650
+
651
+ action :destroy do
652
+ allow :owner, :of => :post
653
+ end
654
+ </code></pre>
655
+
656
+ This is equivalent to:
657
+
658
+ <pre><code>
659
+ allow :admin
660
+
661
+ allow all, :to => [:index, :show]
662
+ allow :managers, :of => Post, :to => [:new, :create]
663
+ allow :owner, :of => :post, :to => [:edit, :update]
664
+ allow :owner, :of => :post, :to => :destroy
665
+ </code></pre>
666
+
667
+ Note that only @allow@ and @deny@ calls are available inside @actions@ block, and these may not have
668
+ @:to@/@:except@ options.
669
+
670
+ @action@ is just a synonym for @actions@.
671
+
672
+ h2. access_control method
673
+
674
+ By calling @access_control@ in your controller you can get your ACL block translated into...
675
+
676
+ # a lambda, installed with @before_filter@ and raising @Acl9::AccessDenied@ exception on occasion.
677
+ # a method, installed with @before_filter@ and raising @Acl9::AccessDenied@ exception on occasion.
678
+ # a method, returning @true@ or @false@, whether access is allowed or denied.
679
+
680
+ First case is by default. You can catch the exception with @rescue_from@ call and do something
681
+ you like: make a redirect, or render "Access denied" template, or whatever.
682
+
683
+ Second case is obtained with specifying method name as an argument to
684
+ @access_control@ (or using @:as_method@ option, see below) and may be helpful
685
+ if you want to use @skip_before_filter@ somewhere in the derived controller.
686
+
687
+ Third case will take place if you supply @:filter => false@ along with method
688
+ name. You'll get an ordinary method which you can call anywhere you want.
689
+
690
+ h3. :subject_method
691
+
692
+ Acl9 obtains the subject instance by calling specific method of the controller. By default it's
693
+ @:current_user@, but you may change it.
694
+
695
+ <pre><code>
696
+ class MyController < ApplicationController
697
+ access_control :subject_method => :current_account do
698
+ allow :nifty
699
+ # ...
700
+ end
701
+
702
+ # ...
703
+ end
704
+ </code></pre>
705
+
706
+ Subject method can also be changed globally. Place the following into @config/initializers/acl9.rb@:
707
+
708
+ <pre><code>
709
+ Acl9::config[:default_subject_method] = :current_account
710
+ </code></pre>
711
+
712
+ h3. :debug
713
+
714
+ @:debug => true@ will output the filtering expression into the debug log. If
715
+ Acl9 does something strange, you may look at it as the last resort.
716
+
717
+ h3. :as_method
718
+
719
+ In the case
720
+
721
+ <pre><code>
722
+ class NiftyController < ApplicationController
723
+ access_control :as_method => :acl do
724
+ allow :nifty
725
+ # ...
726
+ end
727
+
728
+ # ...
729
+ end
730
+ </code></pre>
731
+
732
+ access control checks will be added as @acl@ method onto MyController, with @before_filter :acl@ call thereafter.
733
+
734
+ Instead of using @:as_method@ you may specify the name of the method as a positional argument
735
+ to @access_control@:
736
+
737
+ <pre><code>
738
+ class MyController < ApplicationController
739
+ access_control :acl do
740
+ # ...
741
+ end
742
+
743
+ # ...
744
+ end
745
+ </code></pre>
746
+
747
+
748
+ h3. :filter
749
+
750
+ If you set @:filter@ to @false@ (it's @true@ by default) and also use
751
+ @:as_method@ (or method name as 1st argument to @access_control@, you'll get a
752
+ method which won't raise @Acl9::AccessDenied@ exception, but rather return
753
+ @true@ or @false@ (access allowed/denied).
754
+
755
+ <pre><code>
756
+ class SecretController < ApplicationController
757
+ access_control :secret_access?, :filter => false do
758
+ allow :james_bond
759
+ # ...
760
+ end
761
+
762
+ def index
763
+ if secret_access?
764
+ _secret_index
765
+ else
766
+ _ordinary_index
767
+ end
768
+ end
769
+
770
+ # ...
771
+
772
+ private
773
+
774
+ def _secret_index
775
+ # ...
776
+ end
777
+
778
+ def _ordinary_index
779
+ # ...
780
+ end
781
+ end
782
+ </code></pre>
783
+
784
+ The generated method can receive an objects hash as an argument. In this example,
785
+
786
+ <pre><code>
787
+ class LolController < ApplicationController
788
+ access_control :lolcats?, :filter => false do
789
+ allow :cats, :by => :lol
790
+ # ...
791
+ end
792
+ end
793
+ </code></pre>
794
+
795
+ you may not only call @lolcats?@ with no arguments, which will basically return
796
+
797
+ <pre><code>
798
+ current_user.has_role?('cats', @lol)
799
+ </code></pre>
800
+
801
+ but also as @lolcats?(:lol => Lol.find(params[:lol]))@. The hash will be looked into first,
802
+ even if you have an instance variable @lol@.
803
+
804
+ h3. :helper
805
+
806
+ Sometimes you want to have a boolean method (like @:filter => false@) accessible
807
+ in your views. Acl9 can call @helper_method@ for you:
808
+
809
+ <pre><code>
810
+ class LolController < ApplicationController
811
+ access_control :helper => :lolcats? do
812
+ allow :cats, :by => :lol
813
+ # ...
814
+ end
815
+ end
816
+ </code></pre>
817
+
818
+ That's equivalent to
819
+
820
+ <pre><code>
821
+ class LolController < ApplicationController
822
+ access_control :lolcats?, :filter => false do
823
+ allow :cats, :by => :lol
824
+ # ...
825
+ end
826
+
827
+ helper_method :lolcats?
828
+ end
829
+ </code></pre>
830
+
831
+ h3. Other options
832
+
833
+ Other options will be passed to @before_filter@. As such, you may use @:only@ and @:except@ to narrow
834
+ the action scope of the whole ACL block.
835
+
836
+ <pre><code>
837
+ class OmgController < ApplicationController
838
+ access_control :only => [:index, :show] do
839
+ allow all
840
+ deny :banned
841
+ end
842
+
843
+ # ...
844
+ end
845
+ </code></pre>
846
+
847
+ is basically equivalent to
848
+
849
+ <pre><code>
850
+ class OmgController < ApplicationController
851
+ access_control do
852
+ actions :index, :show do
853
+ allow all
854
+ deny :banned
855
+ end
856
+
857
+ allow all, :except => [:index, :show]
858
+ end
859
+
860
+ # ...
861
+ end
862
+ </code></pre>
863
+
864
+ h2. access_control in your helpers
865
+
866
+ Apart from using @:helper@ option for @access_control@ call inside controller, there's a
867
+ way to generate helper methods directly, like this:
868
+
869
+ <pre><code>
870
+ module SettingsHelper
871
+ include Acl9Helpers
872
+
873
+ access_control :show_settings? do
874
+ allow :admin
875
+ allow :settings_manager
876
+ end
877
+ end
878
+ </code></pre>
879
+
880
+ Here we mix in @Acl9Helpers@ module which brings in @access_control@ method and call it,
881
+ obtaining @show_settings?@ method.
882
+
883
+ An imaginary view:
884
+
885
+ <pre><code>
886
+ <% if show_settings? %>
887
+ <%= link_to 'Settings', settings_path %>
888
+ <% end %>
889
+ </code></pre>
890
+
891
+ Copyright (c) 2009 Oleg Dashevskii, released under the MIT license.