ae_declarative_authorization 0.12.1 → 1.0.0

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.
data/README.md DELETED
@@ -1,620 +0,0 @@
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::Controller::DSL` 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::Controller::DSL`.
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.