acl9 0.12.3 → 1.0.0

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