ae_declarative_authorization 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/Appraisals +31 -21
  3. data/CHANGELOG +189 -189
  4. data/Gemfile +7 -7
  5. data/Gemfile.lock +68 -60
  6. data/LICENSE.txt +20 -20
  7. data/README.md +620 -620
  8. data/README.rdoc +597 -597
  9. data/Rakefile +35 -33
  10. data/authorization_rules.dist.rb +20 -20
  11. data/declarative_authorization.gemspec +24 -24
  12. data/gemfiles/rails4252.gemfile +10 -10
  13. data/gemfiles/rails4252.gemfile.lock +126 -0
  14. data/gemfiles/rails4271.gemfile +10 -10
  15. data/gemfiles/rails4271.gemfile.lock +126 -0
  16. data/gemfiles/rails507.gemfile +11 -11
  17. data/gemfiles/rails507.gemfile.lock +136 -0
  18. data/gemfiles/rails516.gemfile +11 -0
  19. data/gemfiles/rails516.gemfile.lock +136 -0
  20. data/gemfiles/rails521.gemfile +11 -0
  21. data/gemfiles/rails521.gemfile.lock +144 -0
  22. data/init.rb +5 -5
  23. data/lib/declarative_authorization.rb +18 -18
  24. data/lib/declarative_authorization/authorization.rb +821 -821
  25. data/lib/declarative_authorization/helper.rb +78 -78
  26. data/lib/declarative_authorization/in_controller.rb +713 -713
  27. data/lib/declarative_authorization/in_model.rb +156 -156
  28. data/lib/declarative_authorization/maintenance.rb +215 -215
  29. data/lib/declarative_authorization/obligation_scope.rb +348 -345
  30. data/lib/declarative_authorization/railsengine.rb +5 -5
  31. data/lib/declarative_authorization/reader.rb +549 -549
  32. data/lib/declarative_authorization/test/helpers.rb +261 -261
  33. data/lib/declarative_authorization/version.rb +3 -3
  34. data/lib/generators/authorization/install/install_generator.rb +77 -77
  35. data/lib/generators/authorization/rules/rules_generator.rb +13 -13
  36. data/lib/generators/authorization/rules/templates/authorization_rules.rb +27 -27
  37. data/lib/tasks/authorization_tasks.rake +89 -89
  38. data/log/test.log +15246 -0
  39. data/pkg/ae_declarative_authorization-0.7.1.gem +0 -0
  40. data/pkg/ae_declarative_authorization-0.8.0.gem +0 -0
  41. data/test/authorization_test.rb +1121 -1121
  42. data/test/controller_filter_resource_access_test.rb +573 -573
  43. data/test/controller_test.rb +478 -478
  44. data/test/database.yml +3 -3
  45. data/test/dsl_reader_test.rb +178 -178
  46. data/test/functional/filter_access_to_with_id_in_scope_test.rb +88 -88
  47. data/test/functional/no_filter_access_to_test.rb +79 -79
  48. data/test/functional/params_block_arity_test.rb +39 -39
  49. data/test/helper_test.rb +248 -248
  50. data/test/maintenance_test.rb +46 -46
  51. data/test/model_test.rb +1840 -1840
  52. data/test/profiles/access_checking +20 -0
  53. data/test/schema.sql +60 -60
  54. data/test/test_helper.rb +174 -174
  55. data/test/test_support/minitest_compatibility.rb +26 -26
  56. metadata +17 -5
@@ -1,107 +1,115 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ae_declarative_authorization (0.7.0)
4
+ ae_declarative_authorization (0.8.0)
5
5
  blockenspiel (~> 0.5.0)
6
- rails (>= 4.2.5.2, < 5.1)
6
+ rails (>= 4.2.5.2, < 6)
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
- actioncable (5.0.6)
12
- actionpack (= 5.0.6)
13
- nio4r (>= 1.2, < 3.0)
14
- websocket-driver (~> 0.6.1)
15
- actionmailer (5.0.6)
16
- actionpack (= 5.0.6)
17
- actionview (= 5.0.6)
18
- activejob (= 5.0.6)
11
+ actioncable (5.2.1)
12
+ actionpack (= 5.2.1)
13
+ nio4r (~> 2.0)
14
+ websocket-driver (>= 0.6.1)
15
+ actionmailer (5.2.1)
16
+ actionpack (= 5.2.1)
17
+ actionview (= 5.2.1)
18
+ activejob (= 5.2.1)
19
19
  mail (~> 2.5, >= 2.5.4)
20
20
  rails-dom-testing (~> 2.0)
21
- actionpack (5.0.6)
22
- actionview (= 5.0.6)
23
- activesupport (= 5.0.6)
21
+ actionpack (5.2.1)
22
+ actionview (= 5.2.1)
23
+ activesupport (= 5.2.1)
24
24
  rack (~> 2.0)
25
- rack-test (~> 0.6.3)
25
+ rack-test (>= 0.6.3)
26
26
  rails-dom-testing (~> 2.0)
27
27
  rails-html-sanitizer (~> 1.0, >= 1.0.2)
28
- actionview (5.0.6)
29
- activesupport (= 5.0.6)
28
+ actionview (5.2.1)
29
+ activesupport (= 5.2.1)
30
30
  builder (~> 3.1)
31
- erubis (~> 2.7.0)
31
+ erubi (~> 1.4)
32
32
  rails-dom-testing (~> 2.0)
33
33
  rails-html-sanitizer (~> 1.0, >= 1.0.3)
34
- activejob (5.0.6)
35
- activesupport (= 5.0.6)
34
+ activejob (5.2.1)
35
+ activesupport (= 5.2.1)
36
36
  globalid (>= 0.3.6)
37
- activemodel (5.0.6)
38
- activesupport (= 5.0.6)
39
- activerecord (5.0.6)
40
- activemodel (= 5.0.6)
41
- activesupport (= 5.0.6)
42
- arel (~> 7.0)
43
- activesupport (5.0.6)
37
+ activemodel (5.2.1)
38
+ activesupport (= 5.2.1)
39
+ activerecord (5.2.1)
40
+ activemodel (= 5.2.1)
41
+ activesupport (= 5.2.1)
42
+ arel (>= 9.0)
43
+ activestorage (5.2.1)
44
+ actionpack (= 5.2.1)
45
+ activerecord (= 5.2.1)
46
+ marcel (~> 0.3.1)
47
+ activesupport (5.2.1)
44
48
  concurrent-ruby (~> 1.0, >= 1.0.2)
45
- i18n (~> 0.7)
49
+ i18n (>= 0.7, < 2)
46
50
  minitest (~> 5.1)
47
51
  tzinfo (~> 1.1)
48
52
  appraisal (2.2.0)
49
53
  bundler
50
54
  rake
51
55
  thor (>= 0.14.0)
52
- arel (7.1.4)
56
+ arel (9.0.0)
53
57
  blockenspiel (0.5.0)
54
58
  builder (3.2.3)
55
59
  concurrent-ruby (1.0.5)
56
- crass (1.0.3)
57
- erubis (2.7.0)
60
+ crass (1.0.4)
61
+ erubi (1.7.1)
58
62
  globalid (0.4.1)
59
63
  activesupport (>= 4.2.0)
60
- i18n (0.9.5)
64
+ i18n (1.1.0)
61
65
  concurrent-ruby (~> 1.0)
62
- loofah (2.1.1)
66
+ loofah (2.2.2)
63
67
  crass (~> 1.0.2)
64
68
  nokogiri (>= 1.5.9)
65
69
  mail (2.7.0)
66
70
  mini_mime (>= 0.1.1)
71
+ marcel (0.3.2)
72
+ mimemagic (~> 0.3.2)
67
73
  metaclass (0.0.4)
68
74
  method_source (0.9.0)
69
- mini_mime (1.0.0)
75
+ mimemagic (0.3.2)
76
+ mini_mime (1.0.1)
70
77
  mini_portile2 (2.3.0)
71
- minitest (5.10.3)
72
- mocha (1.3.0)
78
+ minitest (5.11.3)
79
+ mocha (1.7.0)
73
80
  metaclass (~> 0.0.1)
74
- nio4r (2.2.0)
75
- nokogiri (1.8.1)
81
+ nio4r (2.3.1)
82
+ nokogiri (1.8.4)
76
83
  mini_portile2 (~> 2.3.0)
77
- rack (2.0.3)
78
- rack-test (0.6.3)
79
- rack (>= 1.0)
80
- rails (5.0.6)
81
- actioncable (= 5.0.6)
82
- actionmailer (= 5.0.6)
83
- actionpack (= 5.0.6)
84
- actionview (= 5.0.6)
85
- activejob (= 5.0.6)
86
- activemodel (= 5.0.6)
87
- activerecord (= 5.0.6)
88
- activesupport (= 5.0.6)
84
+ rack (2.0.5)
85
+ rack-test (1.1.0)
86
+ rack (>= 1.0, < 3)
87
+ rails (5.2.1)
88
+ actioncable (= 5.2.1)
89
+ actionmailer (= 5.2.1)
90
+ actionpack (= 5.2.1)
91
+ actionview (= 5.2.1)
92
+ activejob (= 5.2.1)
93
+ activemodel (= 5.2.1)
94
+ activerecord (= 5.2.1)
95
+ activestorage (= 5.2.1)
96
+ activesupport (= 5.2.1)
89
97
  bundler (>= 1.3.0)
90
- railties (= 5.0.6)
98
+ railties (= 5.2.1)
91
99
  sprockets-rails (>= 2.0.0)
92
100
  rails-dom-testing (2.0.3)
93
101
  activesupport (>= 4.2.0)
94
102
  nokogiri (>= 1.6)
95
- rails-html-sanitizer (1.0.3)
96
- loofah (~> 2.0)
97
- railties (5.0.6)
98
- actionpack (= 5.0.6)
99
- activesupport (= 5.0.6)
103
+ rails-html-sanitizer (1.0.4)
104
+ loofah (~> 2.2, >= 2.2.2)
105
+ railties (5.2.1)
106
+ actionpack (= 5.2.1)
107
+ activesupport (= 5.2.1)
100
108
  method_source
101
109
  rake (>= 0.8.7)
102
- thor (>= 0.18.1, < 2.0)
110
+ thor (>= 0.19.0, < 2.0)
103
111
  rake (12.3.1)
104
- sprockets (3.7.1)
112
+ sprockets (3.7.2)
105
113
  concurrent-ruby (~> 1.0)
106
114
  rack (> 1, < 3)
107
115
  sprockets-rails (3.2.1)
@@ -111,9 +119,9 @@ GEM
111
119
  sqlite3 (1.3.13)
112
120
  thor (0.20.0)
113
121
  thread_safe (0.3.6)
114
- tzinfo (1.2.4)
122
+ tzinfo (1.2.5)
115
123
  thread_safe (~> 0.1)
116
- websocket-driver (0.6.5)
124
+ websocket-driver (0.7.0)
117
125
  websocket-extensions (>= 0.1.0)
118
126
  websocket-extensions (0.1.3)
119
127
 
@@ -127,4 +135,4 @@ DEPENDENCIES
127
135
  sqlite3
128
136
 
129
137
  BUNDLED WITH
130
- 1.15.4
138
+ 1.16.3
@@ -1,20 +1,20 @@
1
- Copyright (c) 2017 AppFolio, Inc., Steffen Bartsch
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.
1
+ Copyright (c) 2017 AppFolio, Inc., Steffen Bartsch
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,620 +1,620 @@
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 and 5
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
- ### Install
43
-
44
- Declarative Authorization comes with an installer to make setup easy.
45
-
46
- First, include ae_declarative_authorization in your gemfile.
47
-
48
- ```ruby
49
- gem 'ae_declarative_authorization'
50
- ```
51
-
52
- Next, bundle and install.
53
-
54
- $ bundle
55
- $ rails g authorization:install [UserModel=User] [field:type field:type ...] [--create-user --commit --user-belongs-to-role]
56
-
57
- This installer will create a Role model, an admin and a user role, and set a
58
- has_and_belongs_to_many relationship between the User model and the Role model.
59
- It will also add a `role_symbols` method to the user model to meet
60
- declarative_authorization's requirements. The default User model is User. You can override this by simply typing the name of a model as above.
61
-
62
- You can create the model with the fields provided by using the `--create-user` option.
63
-
64
- The `--commit` option will run `rake db:migrate` and `rake db:seed`.
65
-
66
- The `--user-belongs-to-role` option will set up a one-to-many relationship between Users and Roles.
67
- That is, each user has a role_id column and can only have one role. Role inheritance can be used
68
- in authorization rules.
69
-
70
- Finally, the installer also copies default authorization rules, as below.
71
-
72
- ### Generate Authorization Rules
73
-
74
- To copy a default set of authorization rules which includes CRUD priveleges, run:
75
-
76
- $ rails g authorization:rules
77
-
78
- This command will copy the following to `config/authorization_rules.rb`. Remember
79
- to implement the requirements of this gem as described in the Installation section
80
- at the end of this README if you do not use the above installer.
81
-
82
- ```ruby
83
- authorization do
84
- role :guest do
85
- # add permissions for guests here, e.g.
86
- # has_permission_on :conferences, :to => :read
87
- end
88
-
89
- # permissions on other roles, such as
90
- # role :admin do
91
- # has_permission_on :conferences, :to => :manage
92
- # end
93
- # role :user do
94
- # has_permission_on :conferences, :to => [:read, :create]
95
- # has_permission_on :conferences, :to => [:update, :delete] do
96
- # if_attribute :user_id => is {user.id}
97
- # end
98
- # end
99
- # See the readme or GitHub for more examples
100
- end
101
-
102
- privileges do
103
- # default privilege hierarchies to facilitate RESTful Rails apps
104
- privilege :manage, :includes => [:create, :read, :update, :delete]
105
- privilege :create, :includes => :new
106
- privilege :read, :includes => [:index, :show]
107
- privilege :update, :includes => :edit
108
- privilege :delete, :includes => :destroy
109
- end
110
- ```
111
-
112
- ### Controller Authorization
113
-
114
- For RESTful controllers, add `filter_resource_access`:
115
-
116
- ```ruby
117
- class MyRestfulController < ApplicationController
118
- filter_resource_access
119
- end
120
- ```
121
-
122
- For a non-RESTful controller, you can use `filter_access_to`:
123
-
124
- ```ruby
125
- class MyOtherController < ApplicationController
126
- filter_access_to :all
127
- # or a group: filter_access_to [:action1, :action2]
128
- end
129
- ```
130
-
131
-
132
- ### View Authorization
133
-
134
- Declarative Authorization will use `current_user` to check authorization.
135
-
136
- ```erb
137
- <%= link_to 'Edit Post', edit_post_path(@post) if permitted_to? :update, @post %>
138
- ```
139
-
140
-
141
- ## Authorization Data Model
142
-
143
- ```
144
- ----- App domain ----|-------- Authorization conf ---------|------- App domain ------
145
-
146
- includes includes
147
- .--. .---.
148
- | v | v
149
- .------. can_play .------. has_permission .------------. requires .----------.
150
- | User |----------->| Role |----------------->| Permission |<-----------| Activity |
151
- '------' * * '------' * * '------------' 1 * '----------'
152
- |
153
- .-------+------.
154
- 1 / | 1 \ *
155
- .-----------. .---------. .-----------.
156
- | Privilege | | Context | | Attribute |
157
- '-----------' '---------' '-----------'
158
- ```
159
-
160
- In the application domain, each *User* may be assigned to *Roles* that should
161
- define the users' job in the application, such as _Administrator_. On the
162
- right-hand side of this diagram, application developers specify which *Permissions*
163
- are necessary for users to perform activities, such as calling a controller action,
164
- viewing parts of a View or acting on records in the database. Note that
165
- Permissions consist of an *Privilege* that is to be performed, such as _read_,
166
- and a *Context* in that the Operation takes place, such as _companies_.
167
-
168
- In the authorization configuration, Permissions are assigned to Roles and Role
169
- and Permission hierarchies are defined. *Attributes* may be employed to allow
170
- authorization according to dynamic information about the context and the
171
- current user, e.g. "only allow access on employees that belong to the
172
- current user's branch."
173
-
174
-
175
- ## Examples
176
-
177
- A fully functional example application can be found at
178
- http://github.com/stffn/decl_auth_demo_app
179
-
180
-
181
- ## Controller
182
-
183
- If authentication is in place, there are two ways to enable user-specific
184
- access control on controller actions. For resource controllers, which more
185
- or less follow the CRUD pattern, `filter_resource_access` is the simplest
186
- approach. It sets up instance variables in before filters and calls
187
- `filter_access_to` with the appropriate parameters to protect the CRUD methods.
188
-
189
- ```ruby
190
- class EmployeesController < ApplicationController
191
- filter_resource_access
192
- end
193
- ```
194
-
195
- See `Authorization::AuthorizationInController::ClassMethods` for options on
196
- nested resources and custom member and collection actions.
197
-
198
- By default, Declarative Authorization will enable `filter_resource_access` compatibility with `strong_parameters`.
199
- If you want to disable this behavior, you can use the `:strong_parameters` option.
200
-
201
- ```ruby
202
- class EmployeesController < ApplicationController
203
- filter_resource_access :strong_parameters => false
204
- end
205
- ```
206
-
207
- If you prefer less magic or your controller has no resemblance with the resource
208
- controllers, directly calling `filter_access_to` may be the better option. Examples
209
- are given in the following. E.g. the privilege index users is required for
210
- action index. This works as a first default configuration for RESTful
211
- controllers, with these privileges easily handled in the authorization
212
- configuration, which will be described below.
213
-
214
- ```ruby
215
- class EmployeesController < ApplicationController
216
- filter_access_to :all
217
- def index
218
- end
219
- end
220
- ```
221
-
222
- When custom actions are added to such a controller, it helps to define more
223
- clearly which privileges are the respective requirements. That is when the
224
- `filter_access_to` call may become more verbose:
225
-
226
- ```ruby
227
- class EmployeesController < ApplicationController
228
- filter_access_to :all
229
-
230
- # this one would be included in :all, but :read seems to be
231
- # a more suitable privilege than :auto_complete_for_user_name
232
- filter_access_to :auto_complete_for_employee_name, :require => :read
233
-
234
- def auto_complete_for_employee_name
235
- end
236
- end
237
- ```
238
-
239
- For some actions it might be necessary to check certain attributes of the
240
- object the action is to be acting on. Then, the object needs to be loaded
241
- before the action's access control is evaluated. On the other hand, some actions
242
- might prefer the authorization to ignore specific attribute checks as the object is
243
- unknown at checking time, so attribute checks and thus automatic loading of
244
- objects needs to be enabled explicitly.
245
-
246
- ```ruby
247
- class EmployeesController < ApplicationController
248
- filter_access_to :update, :attribute_check => true
249
- def update
250
- # @employee is already loaded from param[:id] because of :attribute_check
251
- end
252
- end
253
- ```
254
-
255
- You can provide the needed object through before_actions. This way, you have
256
- full control over the object that the conditions are checked against. Just make
257
- sure, your before_actions occur before any of the `filter_access_to` calls.
258
-
259
- ```ruby
260
- class EmployeesController < ApplicationController
261
- before_action :new_employee_from_params, :only => :create
262
- before_action :new_employee, :only => [:index, :new]
263
- filter_access_to :all, :attribute_check => true
264
-
265
- def create
266
- @employee.save!
267
- end
268
-
269
- protected
270
- def new_employee_from_params
271
- @employee = Employee.new(params[:employee])
272
- end
273
- end
274
- ```
275
-
276
- If the access is denied, a `permission_denied` method is called on the
277
- current_controller, if defined, and the issue is logged.
278
- For further customization of the filters and object loading, have a look at
279
- the complete API documentation of `filter_access_to` in
280
- `Authorization::AuthorizationInController::ClassMethods`.
281
-
282
-
283
- ## Views
284
-
285
- In views, a simple permitted_to? helper makes showing blocks according to the
286
- current user's privileges easy:
287
-
288
- ```erb
289
- <% permitted_to? :create, :employees do %>
290
- <%= link_to 'New', new_employee_path %>
291
- <% end %>
292
- ```
293
-
294
- Only giving a symbol :employees as context prevents any checks of attributes
295
- as there is no object to check against. For example, in case of nested resources
296
- a new object may come in handy:
297
-
298
- ```erb
299
- <% permitted_to? :create, Branch.new(:company => @company) do
300
- # or @company.branches.new
301
- # or even @company.branches %>
302
- <%= link_to 'New', new_company_branch_path(@company) %>
303
- <% end %>
304
- ```
305
-
306
- Lists are straight-forward:
307
-
308
- ```erb
309
- <% for employee in @employees do %>
310
- <%= link_to 'Edit', edit_employee_path(employee) if permitted_to? :update, employee %>
311
- <% end %>
312
- ```
313
-
314
- See also `Authorization::AuthorizationHelper`.
315
-
316
-
317
- ## Models
318
-
319
- There are two distinct features for model security built into this plugin:
320
- authorizing CRUD operations on objects as well as query rewriting to limit
321
- results according to certain privileges.
322
-
323
- See also Authorization::AuthorizationInModel.
324
-
325
-
326
- ### Model security for CRUD operations
327
-
328
- To activate model security, all it takes is an explicit enabling for each
329
- model that model security should be enforced on, i.e.
330
-
331
- ```ruby
332
- class Employee < ActiveRecord::Base
333
- using_access_control
334
- end
335
- ```
336
-
337
- Thus,
338
- `Employee.create(...)`
339
- fails, if the current user is not allowed to `:create` `:employees` according
340
- to the authorization rules. For the application to find out about what
341
- happened if an operation is denied, the filters throw
342
- `Authorization::NotAuthorized` exceptions.
343
-
344
- As access control on read are costly, with possibly lots of objects being
345
- loaded at a time in one query, checks on read need to be activated explicitly by
346
- adding the `:include_read` option.
347
-
348
-
349
- ### Query rewriting through named scopes
350
-
351
- When retrieving large sets of records from databases, any authorization needs
352
- to be integrated into the query in order to prevent inefficient filtering
353
- afterwards and to use LIMIT and OFFSET in SQL statements. To keep authorization
354
- rules out of the source code, this plugin offers query rewriting mechanisms
355
- through named scopes. Thus,
356
-
357
- ```ruby
358
- Employee.with_permissions_to(:read)
359
- ```
360
-
361
- returns all employee records that the current user is authorized to read. In
362
- addition, just like normal named scopes, query rewriting may be chained with
363
- the usual find method:
364
-
365
- ```ruby
366
- Employee.with_permissions_to(:read).find(:all, :conditions => ...)
367
- ```
368
-
369
- If the current user is completely missing the permissions, an
370
- `Authorization::NotAuthorized` exception is raised. Through
371
- `Model.obligation_conditions`, application developers may retrieve
372
- the conditions for manual rewrites.
373
-
374
-
375
- ## Authorization Rules
376
-
377
- Authorization rules are defined in config/authorization_rules.rb
378
- (Or redefine rules files path via `Authorization::AUTH_DSL_FILES`). E.g.
379
-
380
- ```ruby
381
- authorization do
382
- role :admin do
383
- has_permission_on :employees, :to => [:create, :read, :update, :delete]
384
- end
385
- end
386
- ```
387
-
388
- There is a default role `:guest` that is used if a request is not associated
389
- with any user or with a user without any roles. So, if your application has
390
- public pages, `:guest` can be used to allow access for users that are not
391
- logged in. All other roles are application defined and need to be associated
392
- with users by the application.
393
-
394
- If you need to change the default role, you can do so by adding an initializer
395
- that contains the following statement:
396
-
397
- ```ruby
398
- Authorization.default_role = :anonymous
399
- ```
400
-
401
- Privileges, such as :create, may be put into hierarchies to simplify
402
- maintenance. So the example above has the same meaning as
403
-
404
- ```ruby
405
- authorization do
406
- role :admin do
407
- has_permission_on :employees, :to => :manage
408
- end
409
- end
410
-
411
- privileges do
412
- privilege :manage do
413
- includes :create, :read, :update, :delete
414
- end
415
- end
416
- ```
417
-
418
- Privilege hierarchies may be context-specific, e.g. applicable to `:employees`.
419
-
420
- ```ruby
421
- privileges do
422
- privilege :manage, :employees, :includes => :increase_salary
423
- end
424
- ```
425
- For more complex use cases, authorizations need to be based on attributes. Note
426
- that you then also need to set `:attribute_check => true` in controllers for `filter_access_to`.
427
- E.g. if a branch admin should manage only employees of his branch (see
428
- `Authorization::Reader` in the API docs for a full list of available operators):
429
-
430
- ```ruby
431
- authorization do
432
- role :branch_admin do
433
- has_permission_on :employees do
434
- to :manage
435
- # user refers to the current_user when evaluating
436
- if_attribute :branch => is {user.branch}
437
- end
438
- end
439
- end
440
- ```
441
-
442
- To reduce redundancy in has_permission_on blocks, a rule may depend on
443
- permissions on associated objects:
444
-
445
- ```ruby
446
- authorization do
447
- role :branch_admin do
448
- has_permission_on :branches, :to => :manage do
449
- if_attribute :managers => contains {user}
450
- end
451
-
452
- has_permission_on :employees, :to => :manage do
453
- if_permitted_to :manage, :branch
454
- # instead of
455
- # if_attribute :branch => {:managers => contains {user}}
456
- end
457
- end
458
- end
459
- ```
460
-
461
- Lastly, not only privileges may be organized in a hierarchy but roles as well.
462
- Here, project manager inherit the permissions of employees.
463
-
464
- ```ruby
465
- role :project_manager do
466
- includes :employee
467
- end
468
- ```
469
-
470
- See also `Authorization::Reader`.
471
-
472
- ## Testing
473
-
474
- ae_declarative_authorization provides a few helpers to ease the testing with
475
- authorization in mind.
476
-
477
- In your test_helper.rb, to enable the helpers add
478
-
479
- ```ruby
480
- require 'declarative_authorization/maintenance'
481
-
482
- class Test::Unit::TestCase
483
- include Authorization::TestHelper
484
- end
485
- ```
486
-
487
- For using the test helpers with RSpec, just add the following lines to your
488
- spec_helper.rb (somewhere after `require 'spec/rails'`):
489
-
490
- ```ruby
491
- require 'declarative_authorization/maintenance'
492
- include Authorization::TestHelper
493
- ```
494
-
495
- Now, in unit tests, you may deactivate authorization if needed e.g. for test
496
- setup and assume certain identities for tests:
497
-
498
- ```ruby
499
- class EmployeeTest < ActiveSupport::TestCase
500
- def test_should_read
501
- without_access_control do
502
- Employee.create(...)
503
- end
504
-
505
- assert_nothing_raised do
506
- with_user(admin) do
507
- Employee.find(:first)
508
- end
509
- end
510
- end
511
- end
512
- ```
513
-
514
- Or, with RSpec, it would work like this:
515
-
516
- ```ruby
517
- describe Employee do
518
- it 'should read' do
519
- without_access_control do
520
- Employee.create(...)
521
- end
522
-
523
- with_user(admin) do
524
- Employee.find(:first)
525
- end
526
- end
527
- end
528
- ```
529
-
530
- In functional tests, get, posts, etc. may be tested in the name of certain users:
531
-
532
- ```ruby
533
- get_with admin, :index
534
- post_with admin, :update, :employee => {...}
535
- ```
536
-
537
- See `Authorization::TestHelper` for more information.
538
-
539
-
540
- ## Providing the Plugin's Requirements
541
- The requirements are
542
- * Rails >= 4.2.5.2 and Ruby >= 2.1.x
543
- * An authentication mechanism
544
- * A user object returned by Controller#current_user
545
- * An array of role symbols returned by User#role_symbols
546
- * (For model security) Setting Authorization.current_user to the request's user
547
-
548
- Of the various ways to provide these requirements, here is one way employing
549
- restful_authentication.
550
-
551
- * Install restful_authentication
552
- cd vendor/plugins && git clone git://github.com/technoweenie/restful-authentication.git restful_authentication
553
- cd ../.. && ruby script/generate authenticated user sessions
554
- * Move "include AuthenticatedSystem" to ApplicationController
555
- * Add +filter_access_to+ calls as described above.
556
- * If you'd like to use model security, add a before_action that sets the user
557
- globally to your ApplicationController. This is thread-safe.
558
- before_action :set_current_user
559
- protected
560
- def set_current_user
561
- Authorization.current_user = current_user
562
- end
563
-
564
- * Add roles field to the User model through a :+has_many+ association
565
- (this is just one possible approach; you could just as easily use
566
- :+has_many+ :+through+ or a serialized roles array):
567
- * create a migration for table roles
568
-
569
- class CreateRoles < ActiveRecord::Migration
570
- def self.up
571
- create_table "roles" do |t|
572
- t.column :title, :string
573
- t.references :user
574
- end
575
- end
576
-
577
- def self.down
578
- drop_table "roles"
579
- end
580
- end
581
-
582
- * create a model Role,
583
- class Role < ActiveRecord::Base
584
- belongs_to :user
585
- end
586
-
587
- * add +has_many+ :+roles+ to the User model and a roles method that returns the roles
588
- as an Array of Symbols, e.g.
589
- class User < ActiveRecord::Base
590
- has_many :roles
591
- def role_symbols
592
- (roles || []).map {|r| r.title.to_sym}
593
- end
594
- end
595
-
596
- * add roles to your User objects using e.g.
597
- user.roles.create(:title => "admin")
598
-
599
- Note: If you choose to generate an Account model for restful_authentication
600
- instead of a User model as described above, you have to customize the
601
- examples and create a ApplicationController#current_user method.
602
-
603
-
604
- ## Debugging Authorization
605
-
606
- Currently, the main means of debugging authorization decisions is logging and
607
- exceptions. Denied access to actions is logged to `warn` or `info`, including
608
- some hints about what went wrong.
609
-
610
- All bang methods throw exceptions which may be used to retrieve more
611
- information about a denied access than a Boolean value.
612
-
613
-
614
- ## License
615
-
616
- Released under MIT license.
617
-
618
- Copyright (c) 2008 Steffen Bartsch, TZI, Universität Bremen, Germany
619
-
620
- Copyright (c) 2011-2017 AppFolio, Inc.
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 and 5
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
+ ### Install
43
+
44
+ Declarative Authorization comes with an installer to make setup easy.
45
+
46
+ First, include ae_declarative_authorization in your gemfile.
47
+
48
+ ```ruby
49
+ gem 'ae_declarative_authorization'
50
+ ```
51
+
52
+ Next, bundle and install.
53
+
54
+ $ bundle
55
+ $ rails g authorization:install [UserModel=User] [field:type field:type ...] [--create-user --commit --user-belongs-to-role]
56
+
57
+ This installer will create a Role model, an admin and a user role, and set a
58
+ has_and_belongs_to_many relationship between the User model and the Role model.
59
+ It will also add a `role_symbols` method to the user model to meet
60
+ declarative_authorization's requirements. The default User model is User. You can override this by simply typing the name of a model as above.
61
+
62
+ You can create the model with the fields provided by using the `--create-user` option.
63
+
64
+ The `--commit` option will run `rake db:migrate` and `rake db:seed`.
65
+
66
+ The `--user-belongs-to-role` option will set up a one-to-many relationship between Users and Roles.
67
+ That is, each user has a role_id column and can only have one role. Role inheritance can be used
68
+ in authorization rules.
69
+
70
+ Finally, the installer also copies default authorization rules, as below.
71
+
72
+ ### Generate Authorization Rules
73
+
74
+ To copy a default set of authorization rules which includes CRUD priveleges, run:
75
+
76
+ $ rails g authorization:rules
77
+
78
+ This command will copy the following to `config/authorization_rules.rb`. Remember
79
+ to implement the requirements of this gem as described in the Installation section
80
+ at the end of this README if you do not use the above installer.
81
+
82
+ ```ruby
83
+ authorization do
84
+ role :guest do
85
+ # add permissions for guests here, e.g.
86
+ # has_permission_on :conferences, :to => :read
87
+ end
88
+
89
+ # permissions on other roles, such as
90
+ # role :admin do
91
+ # has_permission_on :conferences, :to => :manage
92
+ # end
93
+ # role :user do
94
+ # has_permission_on :conferences, :to => [:read, :create]
95
+ # has_permission_on :conferences, :to => [:update, :delete] do
96
+ # if_attribute :user_id => is {user.id}
97
+ # end
98
+ # end
99
+ # See the readme or GitHub for more examples
100
+ end
101
+
102
+ privileges do
103
+ # default privilege hierarchies to facilitate RESTful Rails apps
104
+ privilege :manage, :includes => [:create, :read, :update, :delete]
105
+ privilege :create, :includes => :new
106
+ privilege :read, :includes => [:index, :show]
107
+ privilege :update, :includes => :edit
108
+ privilege :delete, :includes => :destroy
109
+ end
110
+ ```
111
+
112
+ ### Controller Authorization
113
+
114
+ For RESTful controllers, add `filter_resource_access`:
115
+
116
+ ```ruby
117
+ class MyRestfulController < ApplicationController
118
+ filter_resource_access
119
+ end
120
+ ```
121
+
122
+ For a non-RESTful controller, you can use `filter_access_to`:
123
+
124
+ ```ruby
125
+ class MyOtherController < ApplicationController
126
+ filter_access_to :all
127
+ # or a group: filter_access_to [:action1, :action2]
128
+ end
129
+ ```
130
+
131
+
132
+ ### View Authorization
133
+
134
+ Declarative Authorization will use `current_user` to check authorization.
135
+
136
+ ```erb
137
+ <%= link_to 'Edit Post', edit_post_path(@post) if permitted_to? :update, @post %>
138
+ ```
139
+
140
+
141
+ ## Authorization Data Model
142
+
143
+ ```
144
+ ----- App domain ----|-------- Authorization conf ---------|------- App domain ------
145
+
146
+ includes includes
147
+ .--. .---.
148
+ | v | v
149
+ .------. can_play .------. has_permission .------------. requires .----------.
150
+ | User |----------->| Role |----------------->| Permission |<-----------| Activity |
151
+ '------' * * '------' * * '------------' 1 * '----------'
152
+ |
153
+ .-------+------.
154
+ 1 / | 1 \ *
155
+ .-----------. .---------. .-----------.
156
+ | Privilege | | Context | | Attribute |
157
+ '-----------' '---------' '-----------'
158
+ ```
159
+
160
+ In the application domain, each *User* may be assigned to *Roles* that should
161
+ define the users' job in the application, such as _Administrator_. On the
162
+ right-hand side of this diagram, application developers specify which *Permissions*
163
+ are necessary for users to perform activities, such as calling a controller action,
164
+ viewing parts of a View or acting on records in the database. Note that
165
+ Permissions consist of an *Privilege* that is to be performed, such as _read_,
166
+ and a *Context* in that the Operation takes place, such as _companies_.
167
+
168
+ In the authorization configuration, Permissions are assigned to Roles and Role
169
+ and Permission hierarchies are defined. *Attributes* may be employed to allow
170
+ authorization according to dynamic information about the context and the
171
+ current user, e.g. "only allow access on employees that belong to the
172
+ current user's branch."
173
+
174
+
175
+ ## Examples
176
+
177
+ A fully functional example application can be found at
178
+ http://github.com/stffn/decl_auth_demo_app
179
+
180
+
181
+ ## Controller
182
+
183
+ If authentication is in place, there are two ways to enable user-specific
184
+ access control on controller actions. For resource controllers, which more
185
+ or less follow the CRUD pattern, `filter_resource_access` is the simplest
186
+ approach. It sets up instance variables in before filters and calls
187
+ `filter_access_to` with the appropriate parameters to protect the CRUD methods.
188
+
189
+ ```ruby
190
+ class EmployeesController < ApplicationController
191
+ filter_resource_access
192
+ end
193
+ ```
194
+
195
+ See `Authorization::AuthorizationInController::ClassMethods` for options on
196
+ nested resources and custom member and collection actions.
197
+
198
+ By default, Declarative Authorization will enable `filter_resource_access` compatibility with `strong_parameters`.
199
+ If you want to disable this behavior, you can use the `:strong_parameters` option.
200
+
201
+ ```ruby
202
+ class EmployeesController < ApplicationController
203
+ filter_resource_access :strong_parameters => false
204
+ end
205
+ ```
206
+
207
+ If you prefer less magic or your controller has no resemblance with the resource
208
+ controllers, directly calling `filter_access_to` may be the better option. Examples
209
+ are given in the following. E.g. the privilege index users is required for
210
+ action index. This works as a first default configuration for RESTful
211
+ controllers, with these privileges easily handled in the authorization
212
+ configuration, which will be described below.
213
+
214
+ ```ruby
215
+ class EmployeesController < ApplicationController
216
+ filter_access_to :all
217
+ def index
218
+ end
219
+ end
220
+ ```
221
+
222
+ When custom actions are added to such a controller, it helps to define more
223
+ clearly which privileges are the respective requirements. That is when the
224
+ `filter_access_to` call may become more verbose:
225
+
226
+ ```ruby
227
+ class EmployeesController < ApplicationController
228
+ filter_access_to :all
229
+
230
+ # this one would be included in :all, but :read seems to be
231
+ # a more suitable privilege than :auto_complete_for_user_name
232
+ filter_access_to :auto_complete_for_employee_name, :require => :read
233
+
234
+ def auto_complete_for_employee_name
235
+ end
236
+ end
237
+ ```
238
+
239
+ For some actions it might be necessary to check certain attributes of the
240
+ object the action is to be acting on. Then, the object needs to be loaded
241
+ before the action's access control is evaluated. On the other hand, some actions
242
+ might prefer the authorization to ignore specific attribute checks as the object is
243
+ unknown at checking time, so attribute checks and thus automatic loading of
244
+ objects needs to be enabled explicitly.
245
+
246
+ ```ruby
247
+ class EmployeesController < ApplicationController
248
+ filter_access_to :update, :attribute_check => true
249
+ def update
250
+ # @employee is already loaded from param[:id] because of :attribute_check
251
+ end
252
+ end
253
+ ```
254
+
255
+ You can provide the needed object through before_actions. This way, you have
256
+ full control over the object that the conditions are checked against. Just make
257
+ sure, your before_actions occur before any of the `filter_access_to` calls.
258
+
259
+ ```ruby
260
+ class EmployeesController < ApplicationController
261
+ before_action :new_employee_from_params, :only => :create
262
+ before_action :new_employee, :only => [:index, :new]
263
+ filter_access_to :all, :attribute_check => true
264
+
265
+ def create
266
+ @employee.save!
267
+ end
268
+
269
+ protected
270
+ def new_employee_from_params
271
+ @employee = Employee.new(params[:employee])
272
+ end
273
+ end
274
+ ```
275
+
276
+ If the access is denied, a `permission_denied` method is called on the
277
+ current_controller, if defined, and the issue is logged.
278
+ For further customization of the filters and object loading, have a look at
279
+ the complete API documentation of `filter_access_to` in
280
+ `Authorization::AuthorizationInController::ClassMethods`.
281
+
282
+
283
+ ## Views
284
+
285
+ In views, a simple permitted_to? helper makes showing blocks according to the
286
+ current user's privileges easy:
287
+
288
+ ```erb
289
+ <% permitted_to? :create, :employees do %>
290
+ <%= link_to 'New', new_employee_path %>
291
+ <% end %>
292
+ ```
293
+
294
+ Only giving a symbol :employees as context prevents any checks of attributes
295
+ as there is no object to check against. For example, in case of nested resources
296
+ a new object may come in handy:
297
+
298
+ ```erb
299
+ <% permitted_to? :create, Branch.new(:company => @company) do
300
+ # or @company.branches.new
301
+ # or even @company.branches %>
302
+ <%= link_to 'New', new_company_branch_path(@company) %>
303
+ <% end %>
304
+ ```
305
+
306
+ Lists are straight-forward:
307
+
308
+ ```erb
309
+ <% for employee in @employees do %>
310
+ <%= link_to 'Edit', edit_employee_path(employee) if permitted_to? :update, employee %>
311
+ <% end %>
312
+ ```
313
+
314
+ See also `Authorization::AuthorizationHelper`.
315
+
316
+
317
+ ## Models
318
+
319
+ There are two distinct features for model security built into this plugin:
320
+ authorizing CRUD operations on objects as well as query rewriting to limit
321
+ results according to certain privileges.
322
+
323
+ See also Authorization::AuthorizationInModel.
324
+
325
+
326
+ ### Model security for CRUD operations
327
+
328
+ To activate model security, all it takes is an explicit enabling for each
329
+ model that model security should be enforced on, i.e.
330
+
331
+ ```ruby
332
+ class Employee < ActiveRecord::Base
333
+ using_access_control
334
+ end
335
+ ```
336
+
337
+ Thus,
338
+ `Employee.create(...)`
339
+ fails, if the current user is not allowed to `:create` `:employees` according
340
+ to the authorization rules. For the application to find out about what
341
+ happened if an operation is denied, the filters throw
342
+ `Authorization::NotAuthorized` exceptions.
343
+
344
+ As access control on read are costly, with possibly lots of objects being
345
+ loaded at a time in one query, checks on read need to be activated explicitly by
346
+ adding the `:include_read` option.
347
+
348
+
349
+ ### Query rewriting through named scopes
350
+
351
+ When retrieving large sets of records from databases, any authorization needs
352
+ to be integrated into the query in order to prevent inefficient filtering
353
+ afterwards and to use LIMIT and OFFSET in SQL statements. To keep authorization
354
+ rules out of the source code, this plugin offers query rewriting mechanisms
355
+ through named scopes. Thus,
356
+
357
+ ```ruby
358
+ Employee.with_permissions_to(:read)
359
+ ```
360
+
361
+ returns all employee records that the current user is authorized to read. In
362
+ addition, just like normal named scopes, query rewriting may be chained with
363
+ the usual find method:
364
+
365
+ ```ruby
366
+ Employee.with_permissions_to(:read).find(:all, :conditions => ...)
367
+ ```
368
+
369
+ If the current user is completely missing the permissions, an
370
+ `Authorization::NotAuthorized` exception is raised. Through
371
+ `Model.obligation_conditions`, application developers may retrieve
372
+ the conditions for manual rewrites.
373
+
374
+
375
+ ## Authorization Rules
376
+
377
+ Authorization rules are defined in config/authorization_rules.rb
378
+ (Or redefine rules files path via `Authorization::AUTH_DSL_FILES`). E.g.
379
+
380
+ ```ruby
381
+ authorization do
382
+ role :admin do
383
+ has_permission_on :employees, :to => [:create, :read, :update, :delete]
384
+ end
385
+ end
386
+ ```
387
+
388
+ There is a default role `:guest` that is used if a request is not associated
389
+ with any user or with a user without any roles. So, if your application has
390
+ public pages, `:guest` can be used to allow access for users that are not
391
+ logged in. All other roles are application defined and need to be associated
392
+ with users by the application.
393
+
394
+ If you need to change the default role, you can do so by adding an initializer
395
+ that contains the following statement:
396
+
397
+ ```ruby
398
+ Authorization.default_role = :anonymous
399
+ ```
400
+
401
+ Privileges, such as :create, may be put into hierarchies to simplify
402
+ maintenance. So the example above has the same meaning as
403
+
404
+ ```ruby
405
+ authorization do
406
+ role :admin do
407
+ has_permission_on :employees, :to => :manage
408
+ end
409
+ end
410
+
411
+ privileges do
412
+ privilege :manage do
413
+ includes :create, :read, :update, :delete
414
+ end
415
+ end
416
+ ```
417
+
418
+ Privilege hierarchies may be context-specific, e.g. applicable to `:employees`.
419
+
420
+ ```ruby
421
+ privileges do
422
+ privilege :manage, :employees, :includes => :increase_salary
423
+ end
424
+ ```
425
+ For more complex use cases, authorizations need to be based on attributes. Note
426
+ that you then also need to set `:attribute_check => true` in controllers for `filter_access_to`.
427
+ E.g. if a branch admin should manage only employees of his branch (see
428
+ `Authorization::Reader` in the API docs for a full list of available operators):
429
+
430
+ ```ruby
431
+ authorization do
432
+ role :branch_admin do
433
+ has_permission_on :employees do
434
+ to :manage
435
+ # user refers to the current_user when evaluating
436
+ if_attribute :branch => is {user.branch}
437
+ end
438
+ end
439
+ end
440
+ ```
441
+
442
+ To reduce redundancy in has_permission_on blocks, a rule may depend on
443
+ permissions on associated objects:
444
+
445
+ ```ruby
446
+ authorization do
447
+ role :branch_admin do
448
+ has_permission_on :branches, :to => :manage do
449
+ if_attribute :managers => contains {user}
450
+ end
451
+
452
+ has_permission_on :employees, :to => :manage do
453
+ if_permitted_to :manage, :branch
454
+ # instead of
455
+ # if_attribute :branch => {:managers => contains {user}}
456
+ end
457
+ end
458
+ end
459
+ ```
460
+
461
+ Lastly, not only privileges may be organized in a hierarchy but roles as well.
462
+ Here, project manager inherit the permissions of employees.
463
+
464
+ ```ruby
465
+ role :project_manager do
466
+ includes :employee
467
+ end
468
+ ```
469
+
470
+ See also `Authorization::Reader`.
471
+
472
+ ## Testing
473
+
474
+ ae_declarative_authorization provides a few helpers to ease the testing with
475
+ authorization in mind.
476
+
477
+ In your test_helper.rb, to enable the helpers add
478
+
479
+ ```ruby
480
+ require 'declarative_authorization/maintenance'
481
+
482
+ class Test::Unit::TestCase
483
+ include Authorization::TestHelper
484
+ end
485
+ ```
486
+
487
+ For using the test helpers with RSpec, just add the following lines to your
488
+ spec_helper.rb (somewhere after `require 'spec/rails'`):
489
+
490
+ ```ruby
491
+ require 'declarative_authorization/maintenance'
492
+ include Authorization::TestHelper
493
+ ```
494
+
495
+ Now, in unit tests, you may deactivate authorization if needed e.g. for test
496
+ setup and assume certain identities for tests:
497
+
498
+ ```ruby
499
+ class EmployeeTest < ActiveSupport::TestCase
500
+ def test_should_read
501
+ without_access_control do
502
+ Employee.create(...)
503
+ end
504
+
505
+ assert_nothing_raised do
506
+ with_user(admin) do
507
+ Employee.find(:first)
508
+ end
509
+ end
510
+ end
511
+ end
512
+ ```
513
+
514
+ Or, with RSpec, it would work like this:
515
+
516
+ ```ruby
517
+ describe Employee do
518
+ it 'should read' do
519
+ without_access_control do
520
+ Employee.create(...)
521
+ end
522
+
523
+ with_user(admin) do
524
+ Employee.find(:first)
525
+ end
526
+ end
527
+ end
528
+ ```
529
+
530
+ In functional tests, get, posts, etc. may be tested in the name of certain users:
531
+
532
+ ```ruby
533
+ get_with admin, :index
534
+ post_with admin, :update, :employee => {...}
535
+ ```
536
+
537
+ See `Authorization::TestHelper` for more information.
538
+
539
+
540
+ ## Providing the Plugin's Requirements
541
+ The requirements are
542
+ * Rails >= 4.2.5.2 and Ruby >= 2.1.x
543
+ * An authentication mechanism
544
+ * A user object returned by Controller#current_user
545
+ * An array of role symbols returned by User#role_symbols
546
+ * (For model security) Setting Authorization.current_user to the request's user
547
+
548
+ Of the various ways to provide these requirements, here is one way employing
549
+ restful_authentication.
550
+
551
+ * Install restful_authentication
552
+ cd vendor/plugins && git clone git://github.com/technoweenie/restful-authentication.git restful_authentication
553
+ cd ../.. && ruby script/generate authenticated user sessions
554
+ * Move "include AuthenticatedSystem" to ApplicationController
555
+ * Add +filter_access_to+ calls as described above.
556
+ * If you'd like to use model security, add a before_action that sets the user
557
+ globally to your ApplicationController. This is thread-safe.
558
+ before_action :set_current_user
559
+ protected
560
+ def set_current_user
561
+ Authorization.current_user = current_user
562
+ end
563
+
564
+ * Add roles field to the User model through a :+has_many+ association
565
+ (this is just one possible approach; you could just as easily use
566
+ :+has_many+ :+through+ or a serialized roles array):
567
+ * create a migration for table roles
568
+
569
+ class CreateRoles < ActiveRecord::Migration
570
+ def self.up
571
+ create_table "roles" do |t|
572
+ t.column :title, :string
573
+ t.references :user
574
+ end
575
+ end
576
+
577
+ def self.down
578
+ drop_table "roles"
579
+ end
580
+ end
581
+
582
+ * create a model Role,
583
+ class Role < ActiveRecord::Base
584
+ belongs_to :user
585
+ end
586
+
587
+ * add +has_many+ :+roles+ to the User model and a roles method that returns the roles
588
+ as an Array of Symbols, e.g.
589
+ class User < ActiveRecord::Base
590
+ has_many :roles
591
+ def role_symbols
592
+ (roles || []).map {|r| r.title.to_sym}
593
+ end
594
+ end
595
+
596
+ * add roles to your User objects using e.g.
597
+ user.roles.create(:title => "admin")
598
+
599
+ Note: If you choose to generate an Account model for restful_authentication
600
+ instead of a User model as described above, you have to customize the
601
+ examples and create a ApplicationController#current_user method.
602
+
603
+
604
+ ## Debugging Authorization
605
+
606
+ Currently, the main means of debugging authorization decisions is logging and
607
+ exceptions. Denied access to actions is logged to `warn` or `info`, including
608
+ some hints about what went wrong.
609
+
610
+ All bang methods throw exceptions which may be used to retrieve more
611
+ information about a denied access than a Boolean value.
612
+
613
+
614
+ ## License
615
+
616
+ Released under MIT license.
617
+
618
+ Copyright (c) 2008 Steffen Bartsch, TZI, Universität Bremen, Germany
619
+
620
+ Copyright (c) 2011-2017 AppFolio, Inc.