be9-acl9 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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