zeiv-declarative_authorization 1.0.0.pre

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +189 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +632 -0
  5. data/Rakefile +53 -0
  6. data/app/controllers/authorization_rules_controller.rb +258 -0
  7. data/app/controllers/authorization_usages_controller.rb +22 -0
  8. data/app/helpers/authorization_rules_helper.rb +218 -0
  9. data/app/views/authorization_rules/_change.erb +58 -0
  10. data/app/views/authorization_rules/_show_graph.erb +44 -0
  11. data/app/views/authorization_rules/_suggestions.erb +48 -0
  12. data/app/views/authorization_rules/change.html.erb +169 -0
  13. data/app/views/authorization_rules/graph.dot.erb +68 -0
  14. data/app/views/authorization_rules/graph.html.erb +47 -0
  15. data/app/views/authorization_rules/index.html.erb +17 -0
  16. data/app/views/authorization_usages/index.html.erb +36 -0
  17. data/authorization_rules.dist.rb +20 -0
  18. data/config/routes.rb +20 -0
  19. data/garlic_example.rb +20 -0
  20. data/init.rb +5 -0
  21. data/lib/declarative_authorization.rb +19 -0
  22. data/lib/declarative_authorization/adapters/active_record.rb +13 -0
  23. data/lib/declarative_authorization/adapters/active_record/base_extensions.rb +0 -0
  24. data/lib/declarative_authorization/adapters/active_record/obligation_scope_builder.rb +0 -0
  25. data/lib/declarative_authorization/authorization.rb +798 -0
  26. data/lib/declarative_authorization/development_support/analyzer.rb +261 -0
  27. data/lib/declarative_authorization/development_support/change_analyzer.rb +253 -0
  28. data/lib/declarative_authorization/development_support/change_supporter.rb +620 -0
  29. data/lib/declarative_authorization/development_support/development_support.rb +243 -0
  30. data/lib/declarative_authorization/helper.rb +68 -0
  31. data/lib/declarative_authorization/in_controller.rb +703 -0
  32. data/lib/declarative_authorization/in_model.rb +188 -0
  33. data/lib/declarative_authorization/maintenance.rb +210 -0
  34. data/lib/declarative_authorization/obligation_scope.rb +361 -0
  35. data/lib/declarative_authorization/rails_legacy.rb +22 -0
  36. data/lib/declarative_authorization/railsengine.rb +6 -0
  37. data/lib/declarative_authorization/reader.rb +546 -0
  38. data/lib/generators/authorization/install/install_generator.rb +77 -0
  39. data/lib/generators/authorization/rules/rules_generator.rb +14 -0
  40. data/lib/generators/authorization/rules/templates/authorization_rules.rb +27 -0
  41. data/lib/tasks/authorization_tasks.rake +89 -0
  42. data/test/authorization_test.rb +1124 -0
  43. data/test/controller_filter_resource_access_test.rb +575 -0
  44. data/test/controller_test.rb +480 -0
  45. data/test/database.yml +3 -0
  46. data/test/dsl_reader_test.rb +178 -0
  47. data/test/helper_test.rb +247 -0
  48. data/test/maintenance_test.rb +46 -0
  49. data/test/model_test.rb +2008 -0
  50. data/test/schema.sql +56 -0
  51. data/test/test_helper.rb +255 -0
  52. metadata +95 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad1380e232796fe64067eb0a878a2086805a1488
4
+ data.tar.gz: 44e79c6824c30b5088957ee466277f229fcfc334
5
+ SHA512:
6
+ metadata.gz: b794aba5aa51b7839cbe130b835189854e700a8920dcb7f002dcffe407fbbbdebb830148f91037e3c19b30b5e6ea662e4ed0936e5b726bb285439d77a3bff270
7
+ data.tar.gz: b0dcfcc4fbc86073210259ed64e85b184adb11b8f19a2847a2ffa7821724a26bd35964c0258ce557ba4c4d5a24e4a0aa70dee4bdc7bd44bd163c8bfc28bfd907
@@ -0,0 +1,189 @@
1
+ ** RELEASE 0.5.7 (Mar 10, 2013)
2
+
3
+ * Ruby 2.0 compatibility [jhawthorn]
4
+
5
+ ** RELEASE 0.5.6 (Sep 23, 2012)
6
+
7
+ * Fix handling of stray object associations [jhawthorn]
8
+
9
+ * Improve test infrastructure [jhawthorn]
10
+
11
+ * Allow decl_auth to be used without ActiveRecord [bterkuile]
12
+
13
+ * Rule reloading in development based on changes [urkle/sb]
14
+
15
+ ** RELEASE 0.5.5 (Jan 10, 2012)
16
+
17
+ * Update of handling of association proxies for Rails 3.2
18
+
19
+ ** RELEASE 0.5.4 (Nov 30, 2011)
20
+
21
+ * Cumulative loading of authorization rules [Damian Curso/sb]
22
+
23
+ * Improved used_privileges rake task [urkle]
24
+
25
+ * Performance improvements [John Hawthorn]
26
+
27
+ ** RELEASE 0.5.3 (May 25, 2011)
28
+
29
+ * Bugfixes and documentation cleanup
30
+
31
+ * Rails 3.1.rc1 compatibility [sb]
32
+
33
+ * Added has_any_role?, has_any_role_with_hierarchy? [t.pickett66]
34
+
35
+ * Allow changing the default role [dbloete]
36
+
37
+ ** RELEASE 0.5.2 (Dec 31, 2010) **
38
+
39
+ * Bugfixes and documentation updates
40
+
41
+ ** RELEASE 0.5.1 (Sep 12, 2010) **
42
+
43
+ ** RELEASE 0.5 (July 21, 2010) **
44
+
45
+ * Ruby 1.9.2 compatibility [sb]
46
+
47
+ * Comparisons in authorization roles: lt, lte, gt, gte [aepstein,hollownest]
48
+
49
+ * DSL optimization: allow array being passed to to
50
+
51
+ * Omnipotent roles [timcharper]
52
+
53
+ * Meaningful error in case of missing authorization rules file [timcharper]
54
+
55
+ * Rails 3 support [sb]
56
+
57
+ * Support shallow nested resources [jjb]
58
+
59
+ * Allow multiple authorization rules files [kaichen]
60
+
61
+ ** RELEASE 0.4 (November 15, 2009) **
62
+
63
+ * Implemented controller namespace handling [sb]
64
+
65
+ * Improved if_attribute to allow nesting of has_many associations [sb]
66
+
67
+ * Improved if_permitted_to: allow has_many associations and improved context inference [sb]
68
+
69
+ * Added option on handling non-existant auto-loaded object [sb]
70
+
71
+ * Added with_user as module method [sb]
72
+
73
+ * Change support i18n [sb]
74
+
75
+ ** RELEASE 0.3.2.3 (October 12, 2009) **
76
+
77
+ * Switched to gemcutter [sb]
78
+
79
+ * Fixed has_role? for guest user. Closes #8 [sb]
80
+
81
+ * Fixed unnecessary DB query with named scopes [sb, ledermann]
82
+
83
+ * Change support: suggestions: grouping, sorting by affected users [sb]
84
+
85
+ * Fixed context inference from AR objects for STI by switching to #class.name.tableize [sb]
86
+
87
+ * Allow multiple contexts as arguments to has_permission_on [Jeroen van Dijk]
88
+
89
+ ** RELEASE 0.3.2.2 (August 27, 2009) **
90
+
91
+ * Fix without_access_control test cases [sb]
92
+
93
+ * Fixed error on debug logging (Closes #6) [sb]
94
+
95
+ * Fixed without_access_control instance method in TestHelper [sb]
96
+
97
+ ** RELEASE 0.3.2.1 (August 14, 2009) **
98
+
99
+ * Fix gemspec for Rdoc generation [sb]
100
+
101
+ ** RELEASE 0.3.2 (August 13, 2009) **
102
+
103
+ * Fix for model-level permitted_to?/! [sb]
104
+
105
+ ** RELEASE 0.3.1 (August 12, 2009) **
106
+
107
+ * Change Support: Suggestion grouping, sort by affected users [sb]
108
+
109
+ * Changed context derived from objects to #class.name.tableize to fix STI [sb]
110
+
111
+ * Simplified controller authorization with filter_resource_access [sb]
112
+
113
+ * Allow passing explicit context in addition to object in permitted_to? [Olly Lylo, sb]
114
+
115
+ * Change Supporter: suggest changes to authorization rules [sb]
116
+
117
+ * Added permitted_to!/? in model [Eike Carls]
118
+
119
+ * New test helper: should_(not_)_be_allowed_to(privilege, object_or_context) [sb]
120
+
121
+ ** RELEASE 0.3 (April 20, 2009) **
122
+
123
+ * New option :join_by for has_permission_on to allow AND'ing of statements in one has_permission_on block [sb]
124
+
125
+ * Allow using_access_control to be called directly on ActiveRecord::Base, globally enabling model security [sb]
126
+
127
+ * New operator: intersects_with, comparing two Enumerables in if_attribute [sb]
128
+
129
+ * Improved if_permitted_to syntax: if the attribute is left out, permissions are checked on for the current object [sb]
130
+
131
+ * Added #has_role_with_hierarchy? method to retrieve explicit and calculated roles [jeremyf]
132
+
133
+ * Added a simple rules analyzer to help improve authorization rules [sb]
134
+
135
+ * Gemified plugin. Needed to restructure the lib path contents [sb]
136
+
137
+ * Added handling of Authorization::AuthorizationInController::ClassMethods.filter_access_to parameters that are of the form [:show, :update] instead of just :show, :update. [jeremyf]
138
+
139
+ * Added authorization usage helper for checking filter_access_to usage in controllers [sb]
140
+
141
+ * Added a authorization rules browser. See README for more information [sb]
142
+
143
+ * Added Model.using_access_control? to check if a model has model security activated [sb]
144
+
145
+ * Changed Authorization::ObligationScope#map_table_alias_for [Brian Langenfeld]
146
+ * Fixed to prevent bad aliases from being produced.
147
+
148
+ * Changed Authorization::Attribute#validate? [Brian Langenfeld]
149
+ * Encountering a nil value when evaluating an attribute now raises a NilAttributeValueError, instead of an AuthorizationError. We leave it to the caller to decide what to do about it.
150
+
151
+ * Changed Authorization::Engine#permit! [Brian Langenfeld]
152
+ * We now convert incoming privileges to symbols (e.g. 'read' is made equivalent to :read). This ensures the privileges will match those defined in the authorization rules file.
153
+ * The method now properly infers context when checking against an association (e.g. user.posts). We do this by leveraging ActiveRecord builder method 'new' to instantiate a proper object we can work with.
154
+ * When testing rules for positive results (via Authorization::Attribute#validate?), we now rescue NilAttributeValueError exceptions, simply causing the rule to return a negative result (instead of barfing).
155
+
156
+ * Changed Authorization::ObligationScope#rebuild_join_options! [Brian Langenfeld]
157
+ * If we're dealing with multiple obligations we have to check (i.e. ones that result in OR'd conditions), we now use :include instead of :joins for our generated scope. This does seem like a kludge, but until ActiveRecord scopes support unions (for checking obligations individually and consolidating the results), we don't have much choice. Something to revisit later, for sure.
158
+
159
+ ** RELEASE 0.2 (February 2, 2009) **
160
+
161
+ * added negative operators: is_not, not_in, does_not_contain [sb]
162
+
163
+ * changed user.roles to user.role_symbols to reduce interferance with associations [sb]
164
+
165
+ * Ruby 1.9 and Rails 2.3 compatibility [sb]
166
+
167
+ * if_permitted_to for has_permission_on blocks for DRYer auth rules [sb]
168
+
169
+ * ObligationScope rewrite of query rewriting [Brian Langenfeld]
170
+
171
+ * changed exception hierarchy to begin at StandardError [sb]
172
+
173
+ * :is_in operator [sb]
174
+
175
+ * added has_role? helper [sb]
176
+
177
+ * made plugin thread-safe [sb]
178
+
179
+ * added maintenance and test helpers [sb]
180
+
181
+ * changed default permission denied response to 403 Forbidden [sb]
182
+
183
+ * descriptions for titles and roles [sb]
184
+
185
+ * fixed for PostgreSQL [Mark Mansour]
186
+
187
+ * improved DSL syntax: allow for array of contexts in has_permission_on [sb]
188
+
189
+ ** RELEASE 0.1 (August 22, 2008) **
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,632 @@
1
+ = Declarative Authorization
2
+
3
+ The declarative authorization plugin offers an authorization mechanism inspired
4
+ by _RBAC_. The most notable distinction to other authorization plugins is the
5
+ declarative approach. That is, authorization rules are not defined
6
+ programmatically in between business logic but in an authorization configuration.
7
+
8
+ With programmatic authorization rules, the developer needs to specify which roles are
9
+ allowed to access a specific controller action or a part of a view, which is
10
+ not DRY. With a growing application code base roles' permissions often
11
+ change and new roles are introduced. Then, at several places of the source code
12
+ the changes have to be implemented, possibly leading to omissions and thus hard
13
+ to find errors. In these cases, a declarative approach as offered by decl_auth
14
+ increases the development and maintenance efficiency.
15
+
16
+
17
+ Plugin features
18
+ * Authorization at controller action level
19
+ * Authorization helpers for Views
20
+ * Authorization at model level
21
+ * Authorize CRUD (Create, Read, Update, Delete) activities
22
+ * Query rewriting to automatically only fetch authorized records
23
+ * DSL for specifying Authorization rules in an authorization configuration
24
+ * Support for Rails 4, with backwards compatibility through Rails 2
25
+
26
+
27
+ Requirements
28
+ * An authentication mechanism
29
+ * User object in Controller#current_user
30
+ * (For model security) Setting Authorization.current_user
31
+ * User objects need to respond to a method :role_symbols that returns an
32
+ array of role symbols
33
+ See below for installation instructions.
34
+
35
+
36
+ There is a decl_auth screencast by Ryan Bates, nicely introducing the main concepts:
37
+ http://railscasts.com/episodes/188-declarative-authorization
38
+
39
+
40
+ = Quick Start
41
+
42
+ === Installer
43
+
44
+ Declarative Authorization comes with an installer to make setup easy.
45
+
46
+ First, include declarative_authorization in your gemfile.
47
+
48
+ #! Gemfile
49
+ gem 'declarative_authorization'
50
+
51
+ Next, bundle and install.
52
+
53
+ $ bundle
54
+ $ rails g authorization:install [UserModel=User] [field:type field:type ...] [--create-user --commit --user-belongs-to-role]
55
+
56
+ This installer will create a Role model, an admin and a user role, and set a
57
+ has_and_belongs_to_many relationship between the User model and the Role model.
58
+ It will also add a `role_symbols` method to the user model to meet
59
+ declarative_authorization's requirements. The default User model is User. You can override this by simply typing the name of a model as above.
60
+
61
+ You can create the model with the fields provided by using the `--create-user` option.
62
+
63
+ The `--commit` option will run `rake db:migrate` and `rake db:seed`.
64
+
65
+ The `--user-belongs-to-role` option will set up a one-to-many relationship between Users and Roles.
66
+ That is, each user has a role_id column and can only have one role. Role inheritance can be used
67
+ in authorization rules.
68
+
69
+ Finally, the installer also copies default authorization rules, as below.
70
+
71
+ === Generate Authorization Rules
72
+
73
+ To copy a default set of authorization rules which includes CRUD priveleges, run:
74
+
75
+ $ rails g authorization:rules
76
+
77
+ This command will copy the following to `config/authorization_rules.rb`. Remember
78
+ to implement the requirements of this gem as described in the Installation section
79
+ at the end of this README if you do not use the above installer.
80
+
81
+ authorization do
82
+ role :guest do
83
+ # add permissions for guests here, e.g.
84
+ # has_permission_on :conferences, :to => :read
85
+ end
86
+
87
+ # permissions on other roles, such as
88
+ # role :admin do
89
+ # has_permission_on :conferences, :to => :manage
90
+ # end
91
+ # role :user do
92
+ # has_permission_on :conferences, :to => [:read, :create]
93
+ # has_permission_on :conferences, :to => [:update, :delete] do
94
+ # if_attribute :user_id => is {user.id}
95
+ # end
96
+ # end
97
+ # See the readme or GitHub for more examples
98
+ end
99
+
100
+ privileges do
101
+ # default privilege hierarchies to facilitate RESTful Rails apps
102
+ privilege :manage, :includes => [:create, :read, :update, :delete]
103
+ privilege :create, :includes => :new
104
+ privilege :read, :includes => [:index, :show]
105
+ privilege :update, :includes => :edit
106
+ privilege :delete, :includes => :destroy
107
+ end
108
+
109
+ === Controller Authorization
110
+
111
+ For RESTful controllers, add `filter_resource_access`:
112
+
113
+ class MyRestfulController < ApplicationController
114
+ filter_resource_access
115
+ ...
116
+ end
117
+
118
+ For a non-RESTful controller, you can use `filter_access_to`:
119
+
120
+ class MyOtherController < ApplicationController
121
+ filter_access_to :all
122
+ # or a group: filter_access_to [:action1, :action2]
123
+ ...
124
+ end
125
+
126
+ === View Authorization
127
+
128
+ Declarative Authorization will use `current_user` to check authorization.
129
+
130
+ <%= link_to 'Edit Post', edit_post_path(@post) if permitted_to? :update, @post %>
131
+
132
+
133
+ = Authorization Data Model
134
+
135
+ ----- App domain ----|-------- Authorization conf ---------|------- App domain ------
136
+
137
+ includes includes
138
+ .--. .---.
139
+ | v | v
140
+ .------. can_play .------. has_permission .------------. requires .----------.
141
+ | User |----------->| Role |----------------->| Permission |<-----------| Activity |
142
+ '------' * * '------' * * '------------' 1 * '----------'
143
+ |
144
+ .-------+------.
145
+ 1 / | 1 \ *
146
+ .-----------. .---------. .-----------.
147
+ | Privilege | | Context | | Attribute |
148
+ '-----------' '---------' '-----------'
149
+
150
+ In the application domain, each *User* may be assigned to *Roles* that should
151
+ define the users' job in the application, such as _Administrator_. On the
152
+ right-hand side of this diagram, application developers specify which *Permissions*
153
+ are necessary for users to perform activities, such as calling a controller action,
154
+ viewing parts of a View or acting on records in the database. Note that
155
+ Permissions consist of an *Privilege* that is to be performed, such as _read_,
156
+ and a *Context* in that the Operation takes place, such as _companies_.
157
+
158
+ In the authorization configuration, Permissions are assigned to Roles and Role
159
+ and Permission hierarchies are defined. *Attributes* may be employed to allow
160
+ authorization according to dynamic information about the context and the
161
+ current user, e.g. "only allow access on employees that belong to the
162
+ current user's branch."
163
+
164
+
165
+ = Examples
166
+
167
+ A fully functional example application can be found at
168
+ http://github.com/stffn/decl_auth_demo_app
169
+
170
+ Details on the demonstrated methods can be found in the API docs, either
171
+ generated by yourself or at http://www.tzi.org/~sbartsch/declarative_authorization
172
+
173
+ == Controller
174
+
175
+ If authentication is in place, there are two ways to enable user-specific
176
+ access control on controller actions. For resource controllers, which more
177
+ or less follow the CRUD pattern, +filter_resource_access+ is the simplest
178
+ approach. It sets up instance variables in before filters and calls
179
+ filter_access_to with the appropriate parameters to protect the CRUD methods.
180
+
181
+ class EmployeesController < ApplicationController
182
+ filter_resource_access
183
+ ...
184
+ end
185
+
186
+ See Authorization::AuthorizationInController::ClassMethods for options on
187
+ nested resources and custom member and collection actions.
188
+
189
+ By default, declarative_authorization will enable filter_resource_access compatibility with strong_parameters in Rails 4. If you want to disable this behavior, you can use the `:strong_parameters` option.
190
+
191
+ class EmployeesController < ApplicationController
192
+ filter_resource_access :strong_parameters => false
193
+ ...
194
+ end
195
+
196
+ Simalarly, you can use `:strong_parameters => true` if you are using strong_parameters in Rails 3.
197
+
198
+ If you prefer less magic or your controller has no resemblance with the resource
199
+ controllers, directly calling filter_access_to may be the better option. Examples
200
+ are given in the following. E.g. the privilege index users is required for
201
+ action index. This works as a first default configuration for RESTful
202
+ controllers, with these privileges easily handled in the authorization
203
+ configuration, which will be described below.
204
+
205
+ class EmployeesController < ApplicationController
206
+ filter_access_to :all
207
+ def index
208
+ ...
209
+ end
210
+ ...
211
+ end
212
+
213
+ When custom actions are added to such a controller, it helps to define more
214
+ clearly which privileges are the respective requirements. That is when the
215
+ filter_access_to call may become more verbose:
216
+
217
+ class EmployeesController < ApplicationController
218
+ filter_access_to :all
219
+ # this one would be included in :all, but :read seems to be
220
+ # a more suitable privilege than :auto_complete_for_user_name
221
+ filter_access_to :auto_complete_for_employee_name, :require => :read
222
+ def auto_complete_for_employee_name
223
+ ...
224
+ end
225
+ ...
226
+ end
227
+
228
+ For some actions it might be necessary to check certain attributes of the
229
+ object the action is to be acting on. Then, the object needs to be loaded
230
+ before the action's access control is evaluated. On the other hand, some actions
231
+ might prefer the authorization to ignore specific attribute checks as the object is
232
+ unknown at checking time, so attribute checks and thus automatic loading of
233
+ objects needs to be enabled explicitly.
234
+
235
+ class EmployeesController < ApplicationController
236
+ filter_access_to :update, :attribute_check => true
237
+ def update
238
+ # @employee is already loaded from param[:id] because of :attribute_check
239
+ end
240
+ end
241
+
242
+ You can provide the needed object through before_filters. This way, you have
243
+ full control over the object that the conditions are checked against. Just make
244
+ sure, your before_filters occur before any of the filter_access_to calls.
245
+
246
+ class EmployeesController < ApplicationController
247
+ before_filter :new_employee_from_params, :only => :create
248
+ before_filter :new_employee, :only => [:index, :new]
249
+ filter_access_to :all, :attribute_check => true
250
+
251
+ def create
252
+ @employee.save!
253
+ end
254
+
255
+ protected
256
+ def new_employee_from_params
257
+ @employee = Employee.new(params[:employee])
258
+ end
259
+ end
260
+
261
+ If the access is denied, a +permission_denied+ method is called on the
262
+ current_controller, if defined, and the issue is logged.
263
+ For further customization of the filters and object loading, have a look at
264
+ the complete API documentation of filter_access_to in
265
+ Authorization::AuthorizationInController::ClassMethods.
266
+
267
+
268
+ == Views
269
+
270
+ In views, a simple permitted_to? helper makes showing blocks according to the
271
+ current user's privileges easy:
272
+
273
+ <% permitted_to? :create, :employees do %>
274
+ <%= link_to 'New', new_employee_path %>
275
+ <% end %>
276
+
277
+ Only giving a symbol :employees as context prevents any checks of attributes
278
+ as there is no object to check against. For example, in case of nested resources
279
+ a new object may come in handy:
280
+
281
+ <% permitted_to? :create, Branch.new(:company => @company) do
282
+ # or @company.branches.new
283
+ # or even @company.branches %>
284
+ <%= link_to 'New', new_company_branch_path(@company) %>
285
+ <% end %>
286
+
287
+ Lists are straight-forward:
288
+
289
+ <% for employee in @employees %>
290
+ <%= link_to 'Edit', edit_employee_path(employee) if permitted_to? :update, employee %>
291
+ <% end %>
292
+
293
+ See also Authorization::AuthorizationHelper.
294
+
295
+
296
+ == Models
297
+
298
+ There are two distinct features for model security built into this plugin:
299
+ authorizing CRUD operations on objects as well as query rewriting to limit
300
+ results according to certain privileges.
301
+
302
+ See also Authorization::AuthorizationInModel.
303
+
304
+ === Model security for CRUD operations
305
+ To activate model security, all it takes is an explicit enabling for each
306
+ model that model security should be enforced on, i.e.
307
+
308
+ class Employee < ActiveRecord::Base
309
+ using_access_control
310
+ ...
311
+ end
312
+
313
+ Thus,
314
+ Employee.create(...)
315
+ fails, if the current user is not allowed to :create :employees according
316
+ to the authorization rules. For the application to find out about what
317
+ happened if an operation is denied, the filters throw
318
+ Authorization::NotAuthorized exceptions.
319
+
320
+ As access control on read are costly, with possibly lots of objects being
321
+ loaded at a time in one query, checks on read need to be activated explicitly by
322
+ adding the :include_read option.
323
+
324
+ === Query rewriting through named scopes
325
+ When retrieving large sets of records from databases, any authorization needs
326
+ to be integrated into the query in order to prevent inefficient filtering
327
+ afterwards and to use LIMIT and OFFSET in SQL statements. To keep authorization
328
+ rules out of the source code, this plugin offers query rewriting mechanisms
329
+ through named scopes. Thus,
330
+
331
+ Employee.with_permissions_to(:read)
332
+
333
+ returns all employee records that the current user is authorized to read. In
334
+ addition, just like normal named scopes, query rewriting may be chained with
335
+ the usual find method:
336
+
337
+ Employee.with_permissions_to(:read).find(:all, :conditions => ...)
338
+
339
+ If the current user is completely missing the permissions, an
340
+ Authorization::NotAuthorized exception is raised. Through
341
+ Model.obligation_conditions, application developers may retrieve
342
+ the conditions for manual rewrites.
343
+
344
+
345
+ == Authorization Rules
346
+
347
+ Authorization rules are defined in config/authorization_rules.rb
348
+ (Or redefine rules files path via +Authorization::AUTH_DSL_FILES+). E.g.
349
+
350
+ authorization do
351
+ role :admin do
352
+ has_permission_on :employees, :to => [:create, :read, :update, :delete]
353
+ end
354
+ end
355
+
356
+ There is a default role :+guest+ that is used if a request is not associated
357
+ with any user or with a user without any roles. So, if your application has
358
+ public pages, :+guest+ can be used to allow access for users that are not
359
+ logged in. All other roles are application defined and need to be associated
360
+ with users by the application.
361
+
362
+ If you need to change the default role, you can do so by adding an initializer
363
+ that contains the following statement:
364
+
365
+ Authorization.default_role = :anonymous
366
+
367
+ Privileges, such as :create, may be put into hierarchies to simplify
368
+ maintenance. So the example above has the same meaning as
369
+
370
+ authorization do
371
+ role :admin do
372
+ has_permission_on :employees, :to => :manage
373
+ end
374
+ end
375
+
376
+ privileges do
377
+ privilege :manage do
378
+ includes :create, :read, :update, :delete
379
+ end
380
+ end
381
+
382
+ Privilege hierarchies may be context-specific, e.g. applicable to :employees.
383
+
384
+ privileges do
385
+ privilege :manage, :employees, :includes => :increase_salary
386
+ end
387
+
388
+ For more complex use cases, authorizations need to be based on attributes. Note
389
+ that you then also need to set :attribute_check => true in controllers for filter_access_to.
390
+ E.g. if a branch admin should manage only employees of his branch (see
391
+ Authorization::Reader in the API docs for a full list of available operators):
392
+
393
+ authorization do
394
+ role :branch_admin do
395
+ has_permission_on :employees do
396
+ to :manage
397
+ # user refers to the current_user when evaluating
398
+ if_attribute :branch => is {user.branch}
399
+ end
400
+ end
401
+ end
402
+
403
+ To reduce redundancy in has_permission_on blocks, a rule may depend on
404
+ permissions on associated objects:
405
+
406
+ authorization do
407
+ role :branch_admin do
408
+ has_permission_on :branches, :to => :manage do
409
+ if_attribute :managers => contains {user}
410
+ end
411
+
412
+ has_permission_on :employees, :to => :manage do
413
+ if_permitted_to :manage, :branch
414
+ # instead of
415
+ #if_attribute :branch => {:managers => contains {user}}
416
+ end
417
+ end
418
+ end
419
+
420
+ Lastly, not only privileges may be organized in a hierarchy but roles as well.
421
+ Here, project manager inherit the permissions of employees.
422
+
423
+ role :project_manager do
424
+ includes :employee
425
+ end
426
+
427
+ See also Authorization::Reader.
428
+
429
+ == Testing
430
+
431
+ declarative_authorization provides a few helpers to ease the testing with
432
+ authorization in mind.
433
+
434
+ In your test_helper.rb, to enable the helpers add
435
+
436
+ require 'declarative_authorization/maintenance'
437
+
438
+ class Test::Unit::TestCase
439
+ include Authorization::TestHelper
440
+ ...
441
+ end
442
+
443
+ For using the test helpers with RSpec, just add the following lines to your
444
+ spec_helper.rb (somewhere after require 'spec/rails'):
445
+
446
+ require 'declarative_authorization/maintenance'
447
+ include Authorization::TestHelper
448
+
449
+ Now, in unit tests, you may deactivate authorization if needed e.g. for test
450
+ setup and assume certain identities for tests:
451
+
452
+ class EmployeeTest < ActiveSupport::TestCase
453
+ def test_should_read
454
+ without_access_control do
455
+ Employee.create(...)
456
+ end
457
+ assert_nothing_raised do
458
+ with_user(admin) do
459
+ Employee.find(:first)
460
+ end
461
+ end
462
+ end
463
+ end
464
+
465
+ Or, with RSpec, it would work like this:
466
+
467
+ describe Employee do
468
+ it "should read" do
469
+ without_access_control do
470
+ Employee.create(...)
471
+ end
472
+ with_user(admin) do
473
+ Employee.find(:first)
474
+ end
475
+ end
476
+ end
477
+
478
+ In functional tests, get, posts, etc. may be tested in the name of certain users:
479
+
480
+ get_with admin, :index
481
+ post_with admin, :update, :employee => {...}
482
+
483
+ See Authorization::TestHelper for more information.
484
+
485
+
486
+ = Installation of declarative_authorization
487
+
488
+ One of three options to install the plugin:
489
+ * Install by Gem: Add to your environment.rb in the initializer block:
490
+ config.gem "declarative_authorization"
491
+ Note: you need gemcutter support in place, i.e. call
492
+ gem install gemcutter
493
+ gem tumble
494
+ And call from your application's root directory
495
+ rake gems:install
496
+ * Alternativelyi, in Rails 2, to install from github, execute in your application's root directory
497
+ cd vendor/plugins && git clone git://github.com/stffn/declarative_authorization.git
498
+
499
+ Then,
500
+ * provide the requirements as noted below,
501
+ * create a basic config/authorization_rules.rb--you might want to take the
502
+ provided example authorization_rules.dist.rb in the plugin root as a starting
503
+ point,
504
+ * add +filter_access_to+, +permitted_to+? and model security as needed.
505
+
506
+ == Providing the Plugin's Requirements
507
+ The requirements are
508
+ * Rails >= 2.2, including 3 and Ruby >= 1.8.6, including 1.9
509
+ * An authentication mechanism
510
+ * A user object returned by Controller#current_user
511
+ * An array of role symbols returned by User#role_symbols
512
+ * (For model security) Setting Authorization.current_user to the request's user
513
+
514
+ Of the various ways to provide these requirements, here is one way employing
515
+ restful_authentication.
516
+
517
+ * Install restful_authentication
518
+ cd vendor/plugins && git clone git://github.com/technoweenie/restful-authentication.git restful_authentication
519
+ cd ../.. && ruby script/generate authenticated user sessions
520
+ * Move "include AuthenticatedSystem" to ApplicationController
521
+ * Add +filter_access_to+ calls as described above.
522
+ * If you'd like to use model security, add a before_filter that sets the user
523
+ globally to your ApplicationController. This is thread-safe.
524
+ before_filter :set_current_user
525
+ protected
526
+ def set_current_user
527
+ Authorization.current_user = current_user
528
+ end
529
+
530
+ * Add roles field to the User model through a :+has_many+ association
531
+ (this is just one possible approach; you could just as easily use
532
+ :+has_many+ :+through+ or a serialized roles array):
533
+ * create a migration for table roles
534
+ class CreateRoles < ActiveRecord::Migration
535
+ def self.up
536
+ create_table "roles" do |t|
537
+ t.column :title, :string
538
+ t.references :user
539
+ end
540
+ end
541
+
542
+ def self.down
543
+ drop_table "roles"
544
+ end
545
+ end
546
+
547
+ * create a model Role,
548
+ class Role < ActiveRecord::Base
549
+ belongs_to :user
550
+ end
551
+
552
+ * add +has_many+ :+roles+ to the User model and a roles method that returns the roles
553
+ as an Array of Symbols, e.g.
554
+ class User < ActiveRecord::Base
555
+ has_many :roles
556
+ def role_symbols
557
+ (roles || []).map {|r| r.title.to_sym}
558
+ end
559
+ end
560
+
561
+ * add roles to your User objects using e.g.
562
+ user.roles.create(:title => "admin")
563
+
564
+ Note: If you choose to generate an Account model for restful_authentication
565
+ instead of a User model as described above, you have to customize the
566
+ examples and create a ApplicationController#current_user method.
567
+
568
+
569
+ == Debugging Authorization
570
+
571
+ Currently, the main means of debugging authorization decisions is logging and
572
+ exceptions. Denied access to actions is logged to +warn+ or +info+, including
573
+ some hints about what went wrong.
574
+
575
+ All bang methods throw exceptions which may be used to retrieve more
576
+ information about a denied access than a Boolean value.
577
+
578
+
579
+ == Authorization Development Support
580
+
581
+ If your authorization rules become more complex, you might be glad to use
582
+ the authorization rules browser that comes with declarative_authorization.
583
+ It has a syntax-highlighted and a graphical view with filtering of the current
584
+ authorization rules.
585
+
586
+ By default, it will only be available in development mode. To use it, add
587
+ the following lines to your authorization_rules.rb for the appropriate role:
588
+
589
+ has_permission_on :authorization_rules, :to => :read
590
+
591
+ Then, point your browser to
592
+ http://localhost/authorization_rules
593
+
594
+ The browser needs Rails 2.3 (for Engine support). The graphical view requires
595
+ Graphviz (which e.g. can be installed through the graphviz package under Debian
596
+ and Ubuntu) and has only been tested under Linux. Note: for Change Support
597
+ you'll need to have a User#login method that returns a non-ambiguous user
598
+ name for identification.
599
+
600
+
601
+ = Help and Contact
602
+
603
+ We have an issue tracker[http://github.com/stffn/declarative_authorization/issues]
604
+ for bugs and feature requests as well as a
605
+ Google Group[http://groups.google.com/group/declarative_authorization] for
606
+ discussions on the usage of the plugin. You are very welcome to contribute.
607
+ Just fork the git repository and create a new issue, send a pull request or
608
+ contact me personally.
609
+
610
+ Maintained by
611
+
612
+ Steffen Bartsch
613
+ TZI, Universität Bremen, Germany
614
+ sbartsch at tzi.org
615
+
616
+
617
+ = Contributors
618
+
619
+ Thanks to John Joseph Bachir, Dennis Blöte, Eike Carls, Damian Caruso, Kai Chen, Erik Dahlstrand,
620
+ Jeroen van Dijk, Alexander Dobriakov, Sebastian Dyck, Ari Epstein, Jeremy Friesen,
621
+ Tim Harper, John Hawthorn, hollownest, Daniel Kristensen, Jeremy Kleindl, Joel Kociolek,
622
+ Benjamin ter Kuile, Brad Langhorst, Brian Langenfeld,
623
+ Georg Ledermann, Geoff Longman, Olly Lylo, Mark Mansour, Thomas Maurer, Kevin Moore,
624
+ Tyler Pickett, Edward Rudd, Sharagoz,
625
+ TJ Singleton, Mike Vincent, Joel Westerberg
626
+
627
+
628
+ = License
629
+
630
+ Copyright (c) 2008 Steffen Bartsch, TZI, Universität Bremen, Germany
631
+ released under the MIT license
632
+