be9-acl9 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/MIT-LICENSE ADDED
@@ -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.
data/Manifest ADDED
@@ -0,0 +1,19 @@
1
+ lib/acl9/config.rb
2
+ lib/acl9/model_extensions/subject.rb
3
+ lib/acl9/model_extensions/object.rb
4
+ lib/acl9/controller_extensions.rb
5
+ lib/acl9/controller_extensions/filter_producer.rb
6
+ lib/acl9/version.rb
7
+ lib/acl9/model_extensions.rb
8
+ lib/acl9.rb
9
+ spec/db/schema.rb
10
+ spec/filter_producer_spec.rb
11
+ spec/spec_helper.rb
12
+ spec/models.rb
13
+ spec/access_control_spec.rb
14
+ spec/roles_spec.rb
15
+ Manifest
16
+ MIT-LICENSE
17
+ Rakefile
18
+ README.textile
19
+ init.rb
data/README.textile ADDED
@@ -0,0 +1,765 @@
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. Installation
42
+
43
+ Acl9 can be installed as a gem from "GitHub":http://github.com.
44
+
45
+ Add the following line to your @config/environment.rb@:
46
+
47
+ <pre><code>
48
+ config.gem "be9-acl9", :source => "http://gems.github.com"
49
+ </pre></code>
50
+
51
+ Then run @rake gems:install@ (with possible @rake gems:unpack@ thereafter) and you're done!
52
+
53
+ Alternatively you can install Acl9 as a plugin:
54
+
55
+ <pre><code>
56
+ script/plugin install git://github.com/be9/acl9.git
57
+ </pre></code>
58
+
59
+ h1. Basics
60
+
61
+ h2. Authorization is not authentication!
62
+
63
+ Both words start with "auth" but have different meaning!
64
+
65
+ *Authentication* is basically a mapping of credentials (username, password) or
66
+ OpenID to specific user account in the system.
67
+
68
+ *Authorization* is an _authenticated_ user's permission to perform some
69
+ specific action somewhere in the system.
70
+
71
+ Acl9 is a authorization solution, so you will need to implement authentication
72
+ by other means. I recommend "Authlogic":http://github.com/binarylogic/authlogic
73
+ for that purpose, as it's simple, clean and at the same time very configurable.
74
+
75
+ h2. Roles
76
+
77
+ Role is an abstraction. You could directly assign permissions to user accounts in
78
+ your system, but you'd not want to! Way more manageable solution is to assign permissions
79
+ to roles and roles further to users.
80
+
81
+ For example, you can have role called _admin_ which has all available permissions. Now
82
+ you may assign this role to several trusted accounts on your system.
83
+
84
+ Acl9 also supports the notion of _object roles_, that is, roles with limited scope.
85
+
86
+ Imagine we are building a magazine site and want to develop a permission system. So, what roles
87
+ and permissions are there?
88
+
89
+ _Journalists_ should be able to create articles in their section and edit their own articles.
90
+
91
+ _Section editors_ should be able to edit and delete all articles in their sections and
92
+ change the _published_ flag.
93
+
94
+ _Editor-in-chief_ should be able to change everything.
95
+
96
+ We clearly see that journalists and section editors are tied to a specific section, whereas
97
+ editor-in-chief is a role with global scope.
98
+
99
+ h2. Role interface
100
+
101
+ All permission checks in Acl9 are boiled down to calls of a single method:
102
+
103
+ @subject.has_role?(role, object)@
104
+
105
+ That should be read as "Does _subject_ have _role_ on _object_?".
106
+
107
+ Subject is an instance of a @User@, or @Account@, or whatever model you use for
108
+ authentication. Object is an instance of any class (including subject class!)
109
+ or @nil@ (in which case it's a global role).
110
+
111
+ Acl9 builtin role control subsystem provides @has_role?@ method for you, but you can
112
+ also implemented it by hand (see _Coming up with your own role implementation_ below).
113
+
114
+ h1. Acl9 role control subsystem
115
+
116
+ Role control subsystem has been lifted from
117
+ "Rails authorization plugin":http://github.com/DocSavage/rails-authorization-plugin,
118
+ but undergone some modifications.
119
+
120
+ It's based on two tables in the database. First, role table, which stores pairs @[role_name, object]@
121
+ where object is a polymorphic link or a class. Second, join table, which joins users and roles.
122
+
123
+ To use this subsystem, you should define a @Role@ model.
124
+
125
+ h2. Role model
126
+
127
+ <pre><code>
128
+ class Role < ActiveRecord::Base
129
+ acts_as_authorization_role
130
+ end
131
+ </code></pre>
132
+
133
+ The structure of @roles@ table is as follows:
134
+
135
+ <pre><code>
136
+ create_table "roles", :force => true do |t|
137
+ t.string "name", :limit => 40
138
+ t.string "authorizable_type", :limit => 40
139
+ t.integer "authorizable_id"
140
+ t.datetime "created_at"
141
+ t.datetime "updated_at"
142
+ end
143
+ </code></pre>
144
+
145
+ Note that you will never use the @Role@ class directly.
146
+
147
+ h2. Subject model
148
+
149
+ <pre><code>
150
+ class User < ActiveRecord::Base
151
+ acts_as_authorization_subject
152
+ end
153
+ </code></pre>
154
+
155
+ You won't need any specific columns in the @users@ table, but
156
+ there should be a join table:
157
+
158
+ <pre><code>
159
+ create_table "roles_users", :id => false, :force => true do |t|
160
+ t.integer "user_id"
161
+ t.integer "role_id"
162
+ t.datetime "created_at"
163
+ t.datetime "updated_at"
164
+ end
165
+ </code></pre>
166
+
167
+ h2. Object model
168
+
169
+ Place @acts_as_authorization_object@ call inside any model you want to act
170
+ as such.
171
+
172
+ <pre><code>
173
+ class Foo < ActiveRecord::Base
174
+ acts_as_authorization_object
175
+ end
176
+
177
+ class Bar < ActiveRecord::Base
178
+ acts_as_authorization_object
179
+ end
180
+ </code></pre>
181
+
182
+ h2. Interface
183
+
184
+ h3. Subject model
185
+
186
+ A call of @acts_as_authorization_subject@ defines following methods on the model:
187
+
188
+ @subject.has_role?(role, object = nil)@. Returns @true@ of @false@ (has or has not).
189
+
190
+ @subject.has_role!(role, object = nil)@. Assigns a @role@ for the @object@ to the @subject@.
191
+ Does nothing is subject already has such a role.
192
+
193
+ @subject.has_no_role!(role, object = nil)@. Unassigns a role from the @subject@.
194
+
195
+ @subject.has_roles_for?(object)@. Does the @subject@ has any roles for @object@? (@true@ of @false@)
196
+
197
+ @subject.has_role_for?(object)@. Same as @has_roles_for?@.
198
+
199
+ @subject.roles_for(object)@. Returns an array of @Role@ instances, corresponding to @subject@ 's roles on
200
+ @object@. E.g. @subject.roles_for(object).map(&:name).sort@ will give you role names in alphabetical order.
201
+
202
+ @subject.has_no_roles_for!(object)@. Unassign any @subject@ 's roles for a given @object@.
203
+
204
+ @subject.has_no_roles!@. Unassign all roles from @subject@.
205
+
206
+ h3. Object model
207
+
208
+ A call of @acts_as_authorization_object@ defines following methods on the model:
209
+
210
+ @object.accepts_role?(role_name, subject)@. An alias for @subject.has_role?(role_name, object)@.
211
+
212
+ @object.accepts_role!(role_name, subject)@. An alias for @subject.has_role!(role_name, object)@.
213
+
214
+ @object.accepts_no_role!(role_name, subject)@. An alias for @subject.has_no_role!(role_name, object)@.
215
+
216
+ @object.accepts_roles_by?(subject)@. An alias for @subject.has_roles_for?(object)@.
217
+
218
+ @object.accepts_role_by?(subject)@. Same as @accepts_roles_by?@.
219
+
220
+ @object.accepts_roles_by(subject)@. An alias for @subject.roles_for(object)@.
221
+
222
+ h2. Custom class names
223
+
224
+ You may want to deviate from default @User@ and @Role@ class names. That can easily be done with
225
+ arguments to @acts_as_...@.
226
+
227
+ Say, you have @Account@ and @AccountRole@:
228
+
229
+ <pre><code>
230
+ class Account < ActiveRecord::Base
231
+ acts_as_authorization_subject :role_class_name => 'AccountRole'
232
+ end
233
+
234
+ class AccountRole < ActiveRecord::Base
235
+ acts_as_authorization_role :subject_class_name => 'Account'
236
+ end
237
+
238
+ class FooBar < ActiveRecord::Base
239
+ acts_as_authorization_object :role_class_name => 'AccountRole', :subject_class_name => 'Account'
240
+ end
241
+ </code></pre>
242
+
243
+ Or... since Acl9 defaults can be changed in a special hash, you can put the following snippet:
244
+
245
+ <pre><code>
246
+ Acl9::config.merge!({
247
+ :default_role_class_name => 'AccountRole',
248
+ :default_subject_class_name => 'Account',
249
+ })
250
+ </code></pre>
251
+
252
+ ... into @config/initializers/acl9.rb@ and get rid of that clunky arguments:
253
+
254
+ <pre><code>
255
+ class Account < ActiveRecord::Base
256
+ acts_as_authorization_subject
257
+ end
258
+
259
+ class AccountRole < ActiveRecord::Base
260
+ acts_as_authorization_role
261
+ end
262
+
263
+ class FooBar < ActiveRecord::Base
264
+ acts_as_authorization_object
265
+ end
266
+ </code></pre>
267
+
268
+ Note that you'll need to change your database structure appropriately:
269
+
270
+ <pre><code>
271
+ create_table "account_roles", :force => true do |t|
272
+ t.string "name", :limit => 40
273
+ t.string "authorizable_type", :limit => 40
274
+ t.integer "authorizable_id"
275
+ t.datetime "created_at"
276
+ t.datetime "updated_at"
277
+ end
278
+
279
+ create_table "account_roles_accounts", :id => false, :force => true do |t|
280
+ t.integer "account_id"
281
+ t.integer "account_role_id"
282
+ t.datetime "created_at"
283
+ t.datetime "updated_at"
284
+ end
285
+ </code></pre>
286
+
287
+ h2. Examples
288
+
289
+ <pre><code>
290
+ user = User.create!
291
+ user.has_role? 'admin' # => false
292
+
293
+ user.has_role! :admin
294
+
295
+ user.has_role? :admin # => true
296
+ </code></pre>
297
+
298
+ @user@ now has global role _admin_. Note that you can specify role name either
299
+ as a string or as a symbol.
300
+
301
+ <pre><code>
302
+ foo = Foo.create!
303
+
304
+ user.has_role? 'admin', foo # => false
305
+
306
+ user.has_role! :manager, foo
307
+
308
+ user.has_role? :manager, foo # => true
309
+ foo.accepts_role? :manager, user # => true
310
+
311
+ user.has_roles_for? foo # => true
312
+ </code></pre>
313
+
314
+ You can see here that global and object roles are distinguished from each other. User
315
+ with global role _admin_ isn't automatically admin of @foo@.
316
+
317
+ However,
318
+
319
+ <pre><code>
320
+ user.has_role? :manager # => true
321
+ </code></pre>
322
+
323
+ That is, if you have an object role, it means that you have a global role with the same name too!
324
+ In other words, you are _manager_ if you manage at least one @foo@ (or a @bar@...).
325
+
326
+ <pre><code>
327
+ bar = Bar.create!
328
+
329
+ user.has_role! :manager, bar
330
+ user.has_no_role! :manager, foo
331
+
332
+ user.has_role? :manager, foo # => false
333
+ user.has_role? :manager # => true
334
+ </code></pre>
335
+
336
+ Our @user@ is no more manager of @foo@, but is now a manager of @bar@.
337
+
338
+ <pre><code>
339
+ user.has_no_roles!
340
+
341
+ user.has_role? :manager # => false
342
+ user.has_role? :admin # => false
343
+ user.roles # => []
344
+ </code></pre>
345
+
346
+ Now @user@ has no roles in the system.
347
+
348
+ h2. Coming up with your own role implementation
349
+
350
+ The described role system with its 2 tables (not counting the @users@ table!)
351
+ might be an overkill for many cases. If all you want is global roles without
352
+ any scope, you'd better off implementing it by hand.
353
+
354
+ The access control subsystem of Acl9 uses only @subject.has_role?@ method, so
355
+ there's no need to implement anything else except for own convenience.
356
+
357
+ For example, if each your user can have only one global role, just add @role@
358
+ column to your @User@ class:
359
+
360
+ <pre><code>
361
+ class User < ActiveRecord::Base
362
+ def has_role?(role_name, obj=nil)
363
+ self.role == role_name
364
+ end
365
+
366
+ def has_role!(role_name, obj=nil)
367
+ self.role = role_name
368
+ save!
369
+ end
370
+ end
371
+ </code></pre>
372
+
373
+ If you need to assign multiple roles to your users, you can use @serialize@
374
+ with role array or a special solution like
375
+ "preference_fu":http://github.com/brennandunn/preference_fu.
376
+
377
+ h1. Access control subsystem
378
+
379
+ By means of access control subsystem you can protect actions of your controller
380
+ from unauthorized access. Acl9 provides a nice DSL for writing access rules.
381
+
382
+ h2. Allow and deny
383
+
384
+ Access control is mostly about allowing and denying. So there two
385
+ basic methods: @allow@ and @deny@. They have the same syntax:
386
+
387
+ <pre><code>
388
+ allow ROLE_LIST, OPTIONS
389
+ deny ROLE_LIST, OPTIONS
390
+ </code></pre>
391
+
392
+ h3. Specifying roles
393
+
394
+ ROLE_LIST is a list of roles (at least 1 role should be there). So,
395
+
396
+ <pre><code>
397
+ allow :manager, :admin
398
+ deny :banned
399
+ </code></pre>
400
+ will match holders of global role _manager_ *and* holders of global role _admin_ as allowed and
401
+ _banned_ as denied.
402
+
403
+ Basically this snippet is equivalent to
404
+
405
+ <pre><code>
406
+ allow :manager
407
+ allow :admin
408
+ deny :banned
409
+ </code></pre>
410
+ which means that roles in argument list is OR'ed for a match, and not AND'ed.
411
+
412
+ Also note that:
413
+ * You may use both strings and :symbols to specify roles (the latter get converted into strings).
414
+ * Role names are singularized before check.
415
+
416
+ The mentioned snippet can thus be written as
417
+
418
+ <pre><code>
419
+ allow :managers, :admins
420
+ deny 'banned'
421
+ </code></pre>
422
+ or even
423
+
424
+ <pre><code>
425
+ allow *%w(managers admins)
426
+ deny 'banned'
427
+ </code></pre>
428
+
429
+ h3. Object and class roles
430
+
431
+ Examples in the previous section were all about global roles. Let's see how we can
432
+ use object and class roles in the ACL block.
433
+
434
+ <pre><code>
435
+ allow :responsible, :for => Widget
436
+ allow :possessor, :of => :foo
437
+ deny :angry, :at => :me
438
+ allow :interested, :in => Future
439
+ deny :short, :on => :time
440
+ deny :hated, :by => :us
441
+ </code></pre>
442
+
443
+ So, to specify an object you use one of the 6 preposition options:
444
+ * :of
445
+ * :at
446
+ * :on
447
+ * :by
448
+ * :for
449
+ * :in
450
+
451
+ They all have the same meaning, use one that makes better English out of your rule.
452
+
453
+ Now, each of these prepositions may point to a Class or a :symbol. In the former case we get
454
+ a class role. E.g. @allow :responsible, :for => Widget@ becomes @subject.has_role?('responsible', Widget)@.
455
+
456
+ Symbol is trickier, it means that the appropriate instance variable of the controller is taken
457
+ as an object.
458
+
459
+ @allow :possessor, :of => :foo@ is translated into
460
+ <code>subject.has_role?('possessor', controller.instance_variable_get('@foo'))</code>.
461
+
462
+ Checking against an instance variable has sense when you have another _before filter_ which is executed
463
+ *before* the one generated by @access_control@, like this:
464
+
465
+ <pre><code>
466
+ class MoorblesController < ApplicationController
467
+ before_filter :load_moorble, :only => [:edit, :update, :destroy]
468
+
469
+ access_control do
470
+ allow :creator, :of => :moorble
471
+
472
+ # ...
473
+ end
474
+
475
+ # ...
476
+
477
+ private
478
+
479
+ def load_moorble
480
+ @moorble = Moorble.find(params[:id])
481
+ end
482
+ end
483
+ </code></pre>
484
+
485
+
486
+
487
+ Note that the object option is applied to all of the roles you specify in the argument list.
488
+ As such,
489
+
490
+ <pre><code>
491
+ allow :devil, :son, :of => God
492
+ </code></pre>
493
+ is equivalent to
494
+
495
+ <pre><code>
496
+ allow :devil, :of => God
497
+ allow :son, :of => God
498
+ </code></pre>
499
+
500
+ but *NOT*
501
+
502
+ <pre><code>
503
+ allow :devil
504
+ allow :son, :of => God
505
+ </code></pre>
506
+
507
+ h3. Pseudo-roles
508
+
509
+ There are three pseudo-roles in the ACL: @all@, @anonymous@ and @logged_in@.
510
+
511
+ @allow all@ will always match (as well as @deny all@).
512
+
513
+ @allow anonymous@ and @deny anonymous@ will match when user is anonymous, i.e. subject is @nil@.
514
+ You may also use a shorter notation: @allow nil@ (@deny nil@).
515
+
516
+ @logged_in@ is direct opposite of @anonymous@, so @allow logged_in@ will match if the user is logged in
517
+ (subject is not @nil@).
518
+
519
+ No role checks are done in either case.
520
+
521
+ h3. Limiting action scope
522
+
523
+ By default rules apply to all actions of the controller. There are two options that
524
+ narrow the scope of the @deny@ or @allow@ rule: @:to@ and @:except@.
525
+
526
+ <pre><code>
527
+ allow :owner, :of => :site, :to => [:delete, :destroy]
528
+ deny anonymous, :except => [:index, :show]
529
+ </code></pre>
530
+
531
+ For the first rule to match not only the current user should be an _owner_ of the _site_, but also
532
+ current action should be _delete_ or _destroy_.
533
+
534
+ In the second rule anonymous user access is denied for all actions, except _index_ and _show_.
535
+
536
+ You may not specify both @:to@ and @:except@.
537
+
538
+ Note that you can use actions block instead of @:to@ (see _Actions block_
539
+ below). You can also use @:only@ and @:except@ options in the
540
+ @access_control@ call which will serve as options of the @before_filter@ and thus
541
+ limit the scope of the whole ACL.
542
+
543
+ h2. Rule matching order
544
+
545
+ Rule matching system is similar to that of Apache web server. There are two modes: _default allow_
546
+ (corresponding to @Order Deny,Allow@ in Apache) and _default deny_ (@Order Allow,Deny@ in Apache).
547
+
548
+ h3. Setting modes
549
+
550
+ Mode is set with a @default@ call.
551
+
552
+ @default :allow@ will set _default allow_ mode.
553
+
554
+ @default :deny@ will set _default deny_ mode. Note that this is the default mode, i.e. it will be on
555
+ if you don't do a @default@ call at all.
556
+
557
+ h3. Matching algorithm
558
+
559
+ First of all, regardless of the mode, all @allow@ matches are OR'ed together and all @deny@ matches
560
+ are OR'ed as well.
561
+
562
+ We'll express this in the following manner:
563
+
564
+ <pre><code>
565
+ ALLOWED = (allow rule 1 matches?) OR ((allow rule 2 matches?) OR ...
566
+ NOT_DENIED = NOT ((deny rule 1 matches?) OR (deny rule 2 matches?) OR ...)
567
+ </code></pre>
568
+
569
+ So, ALLOWED is @true@ when either of the @allow@ rules matches, and NOT_DENIED is @true@ when none
570
+ of the @deny@ rules matches.
571
+
572
+ Let's denote the final result of algorithm as ALLOWANCE. If it's @true@, access is allowed, if @false@ - denied.
573
+
574
+ In the case of _default allow_:
575
+ <pre><code>
576
+ ALLOWANCE = ALLOWED OR NOT_DENIED
577
+ </code></pre>
578
+
579
+ In the case of _default deny_:
580
+ <pre><code>
581
+ ALLOWANCE = ALLOWED AND NOT_DENIED
582
+ </code></pre>
583
+
584
+ Same result as a table:
585
+
586
+ |_. Rule matches |_. Default allow mode |_. Default deny mode |
587
+ | None of the @allow@ and @deny@ rules matched. | Access is allowed. | Access is denied. |
588
+ | Some of the @allow@ rules matched, none of the @deny@ rules matched. | Access is allowed. | Access is allowed. |
589
+ | None of the @allow@ rules matched, some of the @deny@ rules matched. | Access is denied. | Access is denied. |
590
+ | Some of the @allow@ rules matched, some of the @deny@ rules matched. | Access is allowed. | Access is denied. |
591
+
592
+ Apparently _default deny_ mode is stricter, that's because it's on by default.
593
+
594
+ h2. Actions block
595
+
596
+ You may group rules with the help of the @actions@ block.
597
+
598
+ An example from the imaginary @PostsController@:
599
+
600
+ <pre><code>
601
+ allow :admin
602
+
603
+ actions :index, :show do
604
+ allow all
605
+ end
606
+
607
+ actions :new, :create do
608
+ allow :managers, :of => Post
609
+ end
610
+
611
+ actions :edit, :update do
612
+ allow :owner, :of => :post
613
+ end
614
+
615
+ action :destroy do
616
+ allow :owner, :of => :post
617
+ end
618
+ </code></pre>
619
+
620
+ This is equivalent to:
621
+
622
+ <pre><code>
623
+ allow :admin
624
+
625
+ allow all, :to => [:index, :show]
626
+ allow :managers, :of => Post, :to => [:new, :create]
627
+ allow :owner, :of => :post, :to => [:edit, :update]
628
+ allow :owner, :of => :post, :to => :destroy
629
+ </code></pre>
630
+
631
+ Note that only @allow@ and @deny@ calls are available inside @actions@ block, and these may not have
632
+ @:to@/@:except@ options.
633
+
634
+ @action@ is just a synonym for @actions@.
635
+
636
+ h2. access_control method
637
+
638
+ By calling @access_control@ in your controller you can get your ACL block translated into...
639
+
640
+ # a lambda, installed with @before_filter@ and raising @Acl9::AccessDenied@ exception on occasion.
641
+ # a method, installed with @before_filter@ and raising @Acl9::AccessDenied@ exception on occasion.
642
+ # a method, returning @true@ or @false@, whether access is allowed or denied.
643
+
644
+ First case is by default. You can catch the exception with @rescue_from@ call and do something
645
+ you like: make a redirect, or render "Access denied" template, or whatever.
646
+
647
+ Second case is obtained with @:as_method@ option (see below) and may be helpful if you want
648
+ to use @skip_before_filter@ somewhere on the derived controller.
649
+
650
+ Third case will take place if you use @:as_method@ with @:filter => false@. You'll get an ordinary
651
+ method which you can call anywhere you want.
652
+
653
+ h3. :subject_method
654
+
655
+ Acl9 obtains the subject instance by calling specific method of the controller. By default it's
656
+ @:current_user@, but you may change it.
657
+
658
+ <pre><code>
659
+ class MyController < ApplicationController
660
+ access_control :subject_method => :current_account do
661
+ allow :nifty
662
+ # ...
663
+ end
664
+
665
+ # ...
666
+ end
667
+ </code></pre>
668
+
669
+ Subject method can also be changed globally. Place the following into @config/initializers/acl9.rb@:
670
+
671
+ <pre><code>
672
+ Acl9::config[:default_subject_method] = :current_account
673
+ </code></pre>
674
+
675
+ h3. :debug
676
+
677
+ @:debug => true@ will output the filtering expression into the debug log. If
678
+ Acl9 does something strange, you may look at it as the last resort.
679
+
680
+ h3. :as_method
681
+
682
+ In the case
683
+
684
+ <pre><code>
685
+ class NiftyController < ApplicationController
686
+ access_control :as_method => :acl do
687
+ allow :nifty
688
+ # ...
689
+ end
690
+
691
+ # ...
692
+ end
693
+ </code></pre>
694
+
695
+ access control checks will be added as @acl@ method onto MyController, with @before_filter :acl@ call thereafter.
696
+
697
+ h3. :filter
698
+
699
+ If you set @:filter@ to @false@ (it's @true@ by default) and also use @:as_method@, you'll get a method
700
+ which won't raise @Acl9::AccessDenied@ exception, but rather return @true@ or @false@ (access allowed/denied).
701
+
702
+ <pre><code>
703
+ class SecretController < ApplicationController
704
+ access_control :as_method => :secret_access?, :filter => false do
705
+ allow :james_bond
706
+ # ...
707
+ end
708
+
709
+ def index
710
+ if secret_access?
711
+ _secret_index
712
+ else
713
+ _ordinary_index
714
+ end
715
+ end
716
+
717
+ # ...
718
+
719
+ private
720
+
721
+ def _secret_index
722
+ # ...
723
+ end
724
+
725
+ def _ordinary_index
726
+ # ...
727
+ end
728
+ end
729
+ </code></pre>
730
+
731
+ h3. Other options
732
+
733
+ Other options will be passed to @before_filter@. As such, you may use @:only@ and @:except@ to narrow
734
+ the action scope of the whole ACL block.
735
+
736
+ <pre><code>
737
+ class OmgController < ApplicationController
738
+ access_control :only => [:index, :show] do
739
+ allow all
740
+ deny :banned
741
+ end
742
+
743
+ # ...
744
+ end
745
+ </code></pre>
746
+
747
+ is basically equivalent to
748
+
749
+ <pre><code>
750
+ class OmgController < ApplicationController
751
+ access_control do
752
+ actions :index, :show do
753
+ allow all
754
+ deny :banned
755
+ end
756
+
757
+ allow all, :except => [:index, :show]
758
+ end
759
+
760
+ # ...
761
+ end
762
+ </code></pre>
763
+
764
+
765
+ Copyright (c) 2009 Oleg Dashevskii, released under the MIT license