authorization-rails 1.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,451 @@
1
+ = Authorization plugin
2
+
3
+ See the following wiki page for the latest version of this documentation:
4
+
5
+ http://code.google.com/p/rails-authorization-plugin/w/list
6
+
7
+ This plugin provides a flexible way to add authorization to Rails.
8
+
9
+ The authorization process decides whether a user is allowed access to some
10
+ feature. It is distinct from the authentication process, which tries to
11
+ confirm a user is authentic, not an imposter. There are many authentication
12
+ systems available for Rails, e.g., acts_as_authenticated and LoginEngine. This
13
+ authorization system will play nicely with them as long as some simple
14
+ requirements are met:
15
+
16
+ 1. User objects are available that implement a has_role?(role,
17
+ authorizable_object = nil) method. This requirement can be easily
18
+ handled by using acts_as_authorized_user in the User-like class.
19
+
20
+ 2. If you want to use "role of model" authorization expressions, like "owner of
21
+ resource" or "eligible for :award", then your models with roles must
22
+ implement an accepts_role?(role, user) method. This requirement can
23
+ be handled by using acts_as_authorizable in the model class.
24
+
25
+ The authorization plugin provides the following:
26
+
27
+ * A simple way of checking authorization at either the class or instance method
28
+ level using #permit and #permit?
29
+
30
+ * Authorization using roles for the entire application, a model class, or an
31
+ instance of a model (i.e., a particular object).
32
+
33
+ * Some english-like dynamic methods that draw on the defined roles. You will be
34
+ able to use methods like "user.is_fan_of angelina" or "angelina.has_fans?",
35
+ where a 'fan' is only defined in the roles table.
36
+
37
+ * Pick-and-choose a mixin for your desired level of database complexity. For
38
+ all the features, you will want to use "object roles table" (see below)
39
+
40
+
41
+ == Installation
42
+
43
+ Installation Instructions
44
+
45
+ Installation of the Authorization plugin is quick and easy.
46
+
47
+ Step 1
48
+
49
+ Open a terminal and change directory to the root of your
50
+ Ruby on Rails application referred to here as 'Rails.root'. You
51
+ can choose to install the plugin in the standard recommended way,
52
+ or as a Git sub-module.
53
+
54
+ Step 2 (Standard install, recommended)
55
+
56
+ Run the following command in your Rails.root:
57
+
58
+ ./script/plugin install http://rails-authorization-plugin.googlecode.com/svn/trunk/authorization
59
+
60
+ This will install the latest version of the plugin from SVN trunk
61
+ into your Rails.root/vendor/plugins/authorization directory.
62
+
63
+ Step 2 (Alternative install using Git sub-module, for
64
+ advanced users of the Git SCM)
65
+
66
+ The source code for this plugin is maintained in a Git SCM
67
+ repository (The code in the SVN repository here at Google
68
+ Code is a read-only mirror). The Git repository will always
69
+ have the latest version of the code.
70
+
71
+ You can install the plugin using Git sub-modules (which
72
+ are akin to using SVN externals). Installing this way allows
73
+ you to update the plugin code later if needed (but note that
74
+ it will not update any generated code created earlier by this
75
+ plugin such as migrations, you will need to update that manually).
76
+ Also note that if you are deploying your code using Capistrano
77
+ this method may cause issues if you are not careful (e.g. the code
78
+ will be deployed but the sub-modules will not be updated or
79
+ installed at all).
80
+
81
+ From your Rails.root directory run:
82
+
83
+ git submodule add git://github.com/DocSavage/rails-authorization-plugin.git vendor/plugins/authorization
84
+
85
+ You should be able to update this plugin in the future with
86
+ the simple command (again from Rails.root):
87
+
88
+ git submodule update
89
+
90
+
91
+ == Configuration
92
+
93
+ These instructions will show you how to do the initial configuration
94
+ of the plugin.
95
+
96
+ Choose a Mixin Type
97
+
98
+ Hardwired Roles
99
+ This is the simplest way to use the plugin and requires no database.
100
+ Roles are assumed to be coded into the Model classes using the
101
+ has_role?(role, obj = nil) method. This method is however more
102
+ limited in the functionality available to you.
103
+
104
+ Object Roles (Recommended, DB Required)
105
+ The Object Roles Table mixin provides full support for authorization
106
+ expressions within a database by add a polymorphic field to the
107
+ Role table. Because roles have polymorphic associations to an
108
+ authorizable object, we can assign a user to a role for any model
109
+ instance. So you could declare user X to be a moderator for workshop Y,
110
+ or you could make user A be the owner of resource B.
111
+
112
+ The identity module adds a number of dynamic methods that use defined
113
+ roles. The user-like model gets methods like `user.is_moderator_of
114
+ group (sets user to "moderator" of group`), user.is_moderator? (returns
115
+ true/false if user has some role "moderator"), and group.has_moderators
116
+ (returns an array of users that have role "moderator" for the group). If
117
+ you prefer not to have these dynamic methods available, you can simply
118
+ comment out the inclusion of the identity module within object_roles_table.rb.
119
+
120
+ Initial Configuration Instructions
121
+
122
+ Choose one of the installation types identified above and make sure your
123
+ application provides a current_user method or something that returns the
124
+ current user object (resful_authentication provides this out of the box).
125
+
126
+ At the top of your Rails.root/config/environment.rb file add something
127
+ like the following (customized for your controllers and actions of course):
128
+
129
+ ...
130
+
131
+ # Authorization plugin for role based access control
132
+ # You can override default authorization system constants here.
133
+
134
+ # Can be 'object roles' or 'hardwired'
135
+ AUTHORIZATION_MIXIN = "object roles"
136
+
137
+ # NOTE : If you use modular controllers like '/admin/products' be sure
138
+ # to redirect to something like '/sessions' controller (with a leading slash)
139
+ # as shown in the example below or you will not get redirected properly
140
+ #
141
+ # This can be set to a hash or to an explicit path like '/login'
142
+ #
143
+ LOGIN_REQUIRED_REDIRECTION = { :controller => '/sessions', :action => 'new' }
144
+ PERMISSION_DENIED_REDIRECTION = { :controller => '/home', :action => 'index' }
145
+
146
+ # The method your auth scheme uses to store the location to redirect back to
147
+ STORE_LOCATION_METHOD = :store_location
148
+
149
+ # standard rails config below here
150
+ Rails::Initializer.run do |config|
151
+
152
+ ...
153
+
154
+ Set the AUTHORIZATION_MIXIN constant to object roles or hardwired.
155
+ (See init.rb in this plugin for how the role support is mixed in.)
156
+
157
+ Set the LOGIN_REQUIRED_REDIRECTION to match the path or a hash with
158
+ the :controller and :action for your applications login page.
159
+
160
+ Set the PERMISSION_DENIED_REDIRECTION to match the path or a hash
161
+ with the :controller and :action for your applications permission denied page.
162
+
163
+ Set the STORE_LOCATION_METHOD to the method your application uses for
164
+ storing the current URL that the user should return to after
165
+ authentication (e.g. store_location).
166
+
167
+ See the PLUGIN_DIR\lib\authorization.rb file for the default values
168
+ of LOGIN_REQUIRED_REDIRECTION, PERMISSION_DENIED_REDIRECTION and STORE_LOCATION_METHOD.
169
+
170
+
171
+ Create the database tables
172
+
173
+ If you plan to use the object roles method you will need to setup a few
174
+ database tables. We have provided a database migration file
175
+ (Rails 2.0+ compatible) that will make this process easy for you.
176
+ If you plan to use the hardwired mixin, no extra database tables
177
+ are required. and you can skip to the next step.
178
+
179
+ Run the following command from your Rails.root (Note : The generator
180
+ takes a model name as its argument, which at this time must be 'Role'.):
181
+
182
+ ./script/generate role_model Role
183
+
184
+ This will create:
185
+
186
+ Model: Rails.root/app/models/role.rb
187
+ Test: Rails.root/test/unit/role_test.rb
188
+ Fixtures: Rails.root/test/fixtures/roles.yml
189
+ Migration: Rails.root/db/migrate/###_add_role.rb
190
+
191
+ And now you will need to run a database migration from your Rails.root:
192
+
193
+ rake db:migrate
194
+
195
+ Jumpstarting with a mixin
196
+
197
+ Now we need to add the methods needed by each of your models that will
198
+ participate in role based authorization. Typically these models fall into
199
+ two categories, the User model, and all other models that will have
200
+ roles available for use.
201
+
202
+ For a typical installation you would add both mixins to your User model.
203
+
204
+ class User < ActiveRecord::Base
205
+
206
+ # Authorization plugin
207
+ acts_as_authorized_user
208
+ acts_as_authorizable
209
+
210
+ ...
211
+
212
+ Then in each additional model that you want to be able to restrict based
213
+ on role you would add just the acts_as_authorizable mixin like this:
214
+
215
+ class Event < ActiveRecord::Base
216
+
217
+ acts_as_authorizable
218
+
219
+ ...
220
+
221
+ You are done with the configuration!
222
+
223
+
224
+ == The Specifics
225
+
226
+ === permit and permit?
227
+
228
+ permit and permit? take an authorization expression and a hash of options that
229
+ typically includes any objects that need to be queried:
230
+
231
+ permit <authorization expression> [, options hash ]
232
+ permit? <authorization expression> [, options hash ]
233
+
234
+ The difference between permit and permit? is redirection. permit is a
235
+ declarative statement and redirects by default. It can also be used as a class
236
+ or an instance method, gating the access to an entire controller in a
237
+ before_filter fashion.
238
+
239
+ permit? is only an instance method, can be used within expressions, does not
240
+ redirect by default.
241
+
242
+ The authorization expression is a boolean expression made up of permitted
243
+ roles, prepositions, and authorizable models. Examples include "admin" (User
244
+ model assumed), "moderator of :workshop" (looks at options hash and then
245
+ @workshop), "'top salesman' at :company" (multiword roles delimited by single
246
+ quotes), or "scheduled for Exam" (queries class method of Exam).
247
+
248
+ Note that we can use several permitted prepositions ('of', 'for', 'in', 'on',
249
+ 'to', 'at', 'by'). In the discussion below, we assume you use the "of"
250
+ preposition. You can modify the permitted prepositions by changing the constant
251
+ in Authorization::Base::Parser.
252
+
253
+ * If a specified role has no "of <model>" designation, we assume it is a user
254
+ role (i.e., the model is the user-like object).
255
+
256
+ * If an "of model" designation is given but no "model" key/value is supplied in
257
+ the hash, we check if an instance variable @model if it's available.
258
+
259
+ * If the model is capitalized, we assume it's a class and query
260
+ <tt>Model#self.accepts_role?</tt> (the class method) for the
261
+ permission. (Currently only available in ObjectRolesTable mixin.)
262
+
263
+ For each role, a query is sent to the appropriate model object.
264
+
265
+ The grammar for the authorization expression is:
266
+
267
+ <expr> ::= (<expr>) | not <expr> | <term> or <expr> | <term> and <expr> | <term>
268
+ <term> ::= <role> | <role> <preposition> <model>
269
+ <preposition> ::= of | for | in | on | to | at | by
270
+ <model> ::= /:*\w+/
271
+ <role> ::= /\w+/ | /'.*'/
272
+
273
+ Parentheses should be used to clarify permissions. Note that you may prefix the
274
+ model with an optional ":" -- the first versions of Authorization plugin made
275
+ this mandatory but it's now optional since the mandatory preposition makes
276
+ models unambiguous.
277
+
278
+ ==== Options
279
+
280
+ <tt>:allow_guests => false</tt>. We can allow permission processing without a
281
+ current user object. The default is <tt>false</tt>.
282
+
283
+ <tt>:user</tt> => A <tt>user</tt> object.
284
+
285
+ <tt>:get_user_method => method</tt> that will return a <tt>user</tt>
286
+ object. Default is <tt>#current_user</tt>, which is the how
287
+ <tt>acts_as_authenticated</tt> works.
288
+
289
+ <tt>:only => [ :method1, :method2 ]</tt>. Array of methods to apply permit (not
290
+ valid when used in instance methods)
291
+
292
+ <tt>:except => [ :method1, :method2 ]</tt>. Array of methods that won't have
293
+ permission checking (not valid when used in instance methods)
294
+
295
+ <tt>:redirect => bool</tt>. default is <tt>true</tt>. If <tt>false</tt>, permit
296
+ will not redirect to denied page.
297
+
298
+ <tt>:login_required_redirection => path or hash</tt> where user will be
299
+ redirected if not logged in (default is "{ :controller => 'session', :action =>
300
+ 'new' }")
301
+
302
+ <tt>:login_required_message => 'my message'</tt> (default is 'Login is required
303
+ to access the requested page.')
304
+
305
+ <tt>:permission_denied_redirection => path or hash</tt> where user will be
306
+ redirected if logged in but not authorized (default is '')
307
+
308
+ <tt>:permission_denied_message => 'my message</tt> (default is 'Permission
309
+ denied. You cannot access the requested page.')
310
+
311
+ === Setting and getting the roles
312
+
313
+ Roles are set by #has_role and #accepts_role methods that are mixed into the
314
+ User-like object and the authorizable models. User objects can set roles and
315
+ optionally an object scope for that role:
316
+
317
+ user.has_role 'site_admin'
318
+ user.has_role 'moderator', group
319
+ user.has_no_role 'site_admin'
320
+ user.has_no_role 'moderator', group
321
+ user.has_role 'member', Group
322
+
323
+ Note that the last method sets role "member" on a class "Group". Roles can be
324
+ set with three scopes: entire application (no class or object specified), a
325
+ model class, or an instance of a model (i.e., a model object).
326
+
327
+ Models set roles for specific users:
328
+
329
+ a_model.accepts_role 'moderator', user
330
+ a_model.accepts_no_role 'moderator', user
331
+ Model.accepts_role 'class moderator', user
332
+
333
+ The method language has been chosen to aid memory of the argument order. A user
334
+ has a role "foo", so the role string immediately follows has_role. Similarly, a
335
+ model accepts a role "foo", so the role string immediately follows
336
+ accepts_role. Then we append the scope.
337
+
338
+ Sometimes the user-like object might be an authorizable object as well, for example, when you
339
+ allow 'friend' roles for users. In this case, the user-like object can be declared to be
340
+ <tt>acts_as_authorizable</tt> as well as <tt>acts_as_authorized_user</tt>.
341
+
342
+ Role queries follow the same pattern as the setting of roles:
343
+
344
+ user.has_role? 'moderator'
345
+ user.has_role? 'moderator', group
346
+ user.has_role? 'member', Group
347
+
348
+ a_model.accepts_role? 'moderator', user
349
+ Model.accepts_role? 'moderator', user
350
+
351
+ When a user is queried without specifying either a model class or object, it
352
+ returns true if the user has *any* matching role. For example,
353
+ <tt>user.has_role? 'moderator'</tt> returns true if the user is 'moderator' of
354
+ a class, a model object, or just a generic 'moderator'. Note that if you say
355
+ <tt>user.has_role 'moderator'</tt>, the user does not become 'moderator' for
356
+ all classes and model objects; the user simply has a generic role 'moderator'.
357
+
358
+ ==== Dynamic methods through the Identity mixin
359
+
360
+ The Object Roles Table version includes some dynamic methods that use the roles
361
+ table. For example, if you have roles like "eligible", "moderator", and
362
+ "owner", you'll be able to use the following:
363
+
364
+ user.is_eligible_for_what --> returns array of authorizable objects for which user has role "eligible"
365
+ user.is_moderator_of? group --> returns true/false
366
+ user.is_moderator_of group --> sets user to have role "moderator" for object group.
367
+ user.is_administrator --> sets user to have role "administrator" not really tied to any object.
368
+
369
+ Models get has_* methods:
370
+
371
+ group.has_moderators --> returns array of users with role "moderator" on that group
372
+ group.has_moderators? --> returns true/false
373
+
374
+ Allowed prepositions are optional in the above dynamic methods. They are simply
375
+ syntactic sugar. For example, the following are equivalent:
376
+
377
+ user.is_member_of group
378
+ user.is_member_for group
379
+ user.is_member group
380
+
381
+ Allowed prepositions are required in the authorization expressions because they
382
+ are used to distinguish "role" and "role of :model" and "role of Model".
383
+
384
+ If you prefer not to pollute your namespace with these dynamic methods, do not
385
+ include the Identity module in <tt>object_roles_table.rb</tt>.
386
+
387
+ === Pattern of use
388
+
389
+ We expect the application to provide the following methods:
390
+
391
+ ==== #current_user
392
+
393
+ Returns some user object, like an instance of my favorite class,
394
+ <tt>UserFromMars</tt>. A <tt>user</tt> object, from the Authorization
395
+ viewpoint, is simply an object that provides a <tt>has_role?</tt> method.
396
+
397
+ Note that duck typing means we don't care what else the <tt>UserFromMars</tt>
398
+ might be doing. We only care that we can get an id from whatever it is, and we
399
+ can check if a given role string is associated with it. By using
400
+ <tt>acts_as_authorized_user</tt>, we inject what we need into the user object.
401
+
402
+ If you use an authorization expression "admin of :foo", we check permission by
403
+ asking <tt>foo</tt> if it <tt>accepts_role?('admin', user)</tt>. So for each
404
+ model that is used in an expression, we assume that it provides the
405
+ <tt>accepts_role?(role, user)</tt> method.
406
+
407
+ Note that <tt>user</tt> can be <tt>nil</tt> if <tt>:allow_guests => true</tt>.
408
+
409
+ ==== #store_location (optional)
410
+
411
+ This method will be called if authorization fails and the user is about to be
412
+ redirected to the login action. This allows the application to return to the
413
+ desired page after login. If the application doesn't provide this method, the
414
+ method will not be called.
415
+
416
+ The name of the method for storing a location can be modified by changing the
417
+ constant STORE_LOCATION_METHOD in environment.rb. Also, the default login and
418
+ permission denied pages are defined by the constants LOGIN_REQUIRED_REDIRECTION
419
+ and PERMISSION_DENIED_REDIRECTION in authorization.rb and can be overriden in
420
+ your environment.rb.
421
+
422
+ === Conventions
423
+
424
+ Roles specified without the "of model" designation:
425
+
426
+ 1. We see if there is a <tt>current_user</tt> method available that will return
427
+ a user object. This method can be overridden with the <tt>:user</tt> hash.
428
+
429
+ 2. Once a user object is determined, we pass the role to
430
+ <tt>user.has_role?</tt> and expect a true return value if the user has the
431
+ given role.
432
+
433
+ Roles specified with "of model" designation:
434
+
435
+ 1. We attempt to query an object in the options hash that has a matching
436
+ key. Example: <tt>permit "knight for justice", :justice =>
437
+ @abstract_idea</tt>
438
+
439
+ 2. If there is no object with a matching key, we see if there's a matching
440
+ instance variable. Example: @meeting defined before we use <tt>permit
441
+ "moderator of meeting"</tt>
442
+
443
+ 3. Once the model object is determined, we pass the role and user (determined
444
+ in the manner above) to <tt>model.accepts_role?</tt>
445
+
446
+ === More information
447
+
448
+ Information on this plugin and other development can be found at
449
+ the project home page:
450
+
451
+ http://code.google.com/p/rails-authorization-plugin/
@@ -0,0 +1,43 @@
1
+
2
+ INSTRUCTIONS FOR DEVELOPERS:
3
+
4
+ Pushing a read-only copy of the git repo master branch to the google code SVN repo.
5
+ --
6
+
7
+ Based on an article found at :
8
+ http://blog.nanorails.com/articles/2008/1/31/git-to-svn-read-only
9
+
10
+ Setup:
11
+
12
+ Clone a local copy of the git repo from GitHub:
13
+
14
+ 'git clone git@github.com:DocSavage/rails-authorization-plugin.git'
15
+
16
+ cd rails-authorization-plugin
17
+
18
+ edit .git/config and add the following to the end:
19
+
20
+ --
21
+ [svn-remote "googlecode"]
22
+ url = https://rails-authorization-plugin.googlecode.com/svn/trunk
23
+ fetch = :refs/remotes/googlecode
24
+ --
25
+
26
+ run : 'git svn fetch'
27
+
28
+ run : 'git checkout -b local-svn googlecode'
29
+
30
+ run : 'git svn rebase'
31
+
32
+ run : 'git merge master'
33
+
34
+ run : 'git svn dcommit'
35
+
36
+
37
+ Now in the future as new changes are commit to master, do this to publish to GoogleCode:
38
+
39
+ $ git checkout local-svn
40
+ $ git merge master
41
+ $ git svn dcommit
42
+
43
+ And thats it!