ae_declarative_authorization 0.13.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc DELETED
@@ -1,597 +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
- === 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
- == Controller
171
-
172
- If authentication is in place, there are two ways to enable user-specific
173
- access control on controller actions. For resource controllers, which more
174
- or less follow the CRUD pattern, +filter_resource_access+ is the simplest
175
- approach. It sets up instance variables in before filters and calls
176
- filter_access_to with the appropriate parameters to protect the CRUD methods.
177
-
178
- class EmployeesController < ApplicationController
179
- filter_resource_access
180
- ...
181
- end
182
-
183
- See Authorization::Controller::DSL for options on
184
- nested resources and custom member and collection actions.
185
-
186
- 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.
187
-
188
- class EmployeesController < ApplicationController
189
- filter_resource_access :strong_parameters => false
190
- ...
191
- end
192
-
193
- Simalarly, you can use +:strong_parameters => true+ if you are using strong_parameters in Rails 3.
194
-
195
- If you prefer less magic or your controller has no resemblance with the resource
196
- controllers, directly calling filter_access_to may be the better option. Examples
197
- are given in the following. E.g. the privilege index users is required for
198
- action index. This works as a first default configuration for RESTful
199
- controllers, with these privileges easily handled in the authorization
200
- configuration, which will be described below.
201
-
202
- class EmployeesController < ApplicationController
203
- filter_access_to :all
204
- def index
205
- ...
206
- end
207
- ...
208
- end
209
-
210
- When custom actions are added to such a controller, it helps to define more
211
- clearly which privileges are the respective requirements. That is when the
212
- filter_access_to call may become more verbose:
213
-
214
- class EmployeesController < ApplicationController
215
- filter_access_to :all
216
- # this one would be included in :all, but :read seems to be
217
- # a more suitable privilege than :auto_complete_for_user_name
218
- filter_access_to :auto_complete_for_employee_name, :require => :read
219
- def auto_complete_for_employee_name
220
- ...
221
- end
222
- ...
223
- end
224
-
225
- For some actions it might be necessary to check certain attributes of the
226
- object the action is to be acting on. Then, the object needs to be loaded
227
- before the action's access control is evaluated. On the other hand, some actions
228
- might prefer the authorization to ignore specific attribute checks as the object is
229
- unknown at checking time, so attribute checks and thus automatic loading of
230
- objects needs to be enabled explicitly.
231
-
232
- class EmployeesController < ApplicationController
233
- filter_access_to :update, :attribute_check => true
234
- def update
235
- # @employee is already loaded from param[:id] because of :attribute_check
236
- end
237
- end
238
-
239
- You can provide the needed object through before_actions. This way, you have
240
- full control over the object that the conditions are checked against. Just make
241
- sure, your before_actions occur before any of the filter_access_to calls.
242
-
243
- class EmployeesController < ApplicationController
244
- before_action :new_employee_from_params, :only => :create
245
- before_action :new_employee, :only => [:index, :new]
246
- filter_access_to :all, :attribute_check => true
247
-
248
- def create
249
- @employee.save!
250
- end
251
-
252
- protected
253
- def new_employee_from_params
254
- @employee = Employee.new(params[:employee])
255
- end
256
- end
257
-
258
- If the access is denied, a +permission_denied+ method is called on the
259
- current_controller, if defined, and the issue is logged.
260
- For further customization of the filters and object loading, have a look at
261
- the complete API documentation of filter_access_to in
262
- Authorization::Controller::DSL.
263
-
264
-
265
- == Views
266
-
267
- In views, a simple permitted_to? helper makes showing blocks according to the
268
- current user's privileges easy:
269
-
270
- <% permitted_to? :create, :employees do %>
271
- <%= link_to 'New', new_employee_path %>
272
- <% end %>
273
-
274
- Only giving a symbol :employees as context prevents any checks of attributes
275
- as there is no object to check against. For example, in case of nested resources
276
- a new object may come in handy:
277
-
278
- <% permitted_to? :create, Branch.new(:company => @company) do
279
- # or @company.branches.new
280
- # or even @company.branches %>
281
- <%= link_to 'New', new_company_branch_path(@company) %>
282
- <% end %>
283
-
284
- Lists are straight-forward:
285
-
286
- <% for employee in @employees %>
287
- <%= link_to 'Edit', edit_employee_path(employee) if permitted_to? :update, employee %>
288
- <% end %>
289
-
290
- See also Authorization::AuthorizationHelper.
291
-
292
-
293
- == Models
294
-
295
- There are two distinct features for model security built into this plugin:
296
- authorizing CRUD operations on objects as well as query rewriting to limit
297
- results according to certain privileges.
298
-
299
- See also Authorization::AuthorizationInModel.
300
-
301
- === Model security for CRUD operations
302
- To activate model security, all it takes is an explicit enabling for each
303
- model that model security should be enforced on, i.e.
304
-
305
- class Employee < ActiveRecord::Base
306
- using_access_control
307
- ...
308
- end
309
-
310
- Thus,
311
- Employee.create(...)
312
- fails, if the current user is not allowed to :create :employees according
313
- to the authorization rules. For the application to find out about what
314
- happened if an operation is denied, the filters throw
315
- Authorization::NotAuthorized exceptions.
316
-
317
- As access control on read are costly, with possibly lots of objects being
318
- loaded at a time in one query, checks on read need to be activated explicitly by
319
- adding the :include_read option.
320
-
321
- === Query rewriting through named scopes
322
- When retrieving large sets of records from databases, any authorization needs
323
- to be integrated into the query in order to prevent inefficient filtering
324
- afterwards and to use LIMIT and OFFSET in SQL statements. To keep authorization
325
- rules out of the source code, this plugin offers query rewriting mechanisms
326
- through named scopes. Thus,
327
-
328
- Employee.with_permissions_to(:read)
329
-
330
- returns all employee records that the current user is authorized to read. In
331
- addition, just like normal named scopes, query rewriting may be chained with
332
- the usual find method:
333
-
334
- Employee.with_permissions_to(:read).find(:all, :conditions => ...)
335
-
336
- If the current user is completely missing the permissions, an
337
- Authorization::NotAuthorized exception is raised. Through
338
- Model.obligation_conditions, application developers may retrieve
339
- the conditions for manual rewrites.
340
-
341
-
342
- == Authorization Rules
343
-
344
- Authorization rules are defined in config/authorization_rules.rb
345
- (Or redefine rules files path via +Authorization::AUTH_DSL_FILES+). E.g.
346
-
347
- authorization do
348
- role :admin do
349
- has_permission_on :employees, :to => [:create, :read, :update, :delete]
350
- end
351
- end
352
-
353
- There is a default role :+guest+ that is used if a request is not associated
354
- with any user or with a user without any roles. So, if your application has
355
- public pages, :+guest+ can be used to allow access for users that are not
356
- logged in. All other roles are application defined and need to be associated
357
- with users by the application.
358
-
359
- If you need to change the default role, you can do so by adding an initializer
360
- that contains the following statement:
361
-
362
- Authorization.default_role = :anonymous
363
-
364
- Privileges, such as :create, may be put into hierarchies to simplify
365
- maintenance. So the example above has the same meaning as
366
-
367
- authorization do
368
- role :admin do
369
- has_permission_on :employees, :to => :manage
370
- end
371
- end
372
-
373
- privileges do
374
- privilege :manage do
375
- includes :create, :read, :update, :delete
376
- end
377
- end
378
-
379
- Privilege hierarchies may be context-specific, e.g. applicable to :employees.
380
-
381
- privileges do
382
- privilege :manage, :employees, :includes => :increase_salary
383
- end
384
-
385
- For more complex use cases, authorizations need to be based on attributes. Note
386
- that you then also need to set :attribute_check => true in controllers for filter_access_to.
387
- E.g. if a branch admin should manage only employees of his branch (see
388
- Authorization::Reader in the API docs for a full list of available operators):
389
-
390
- authorization do
391
- role :branch_admin do
392
- has_permission_on :employees do
393
- to :manage
394
- # user refers to the current_user when evaluating
395
- if_attribute :branch => is {user.branch}
396
- end
397
- end
398
- end
399
-
400
- To reduce redundancy in has_permission_on blocks, a rule may depend on
401
- permissions on associated objects:
402
-
403
- authorization do
404
- role :branch_admin do
405
- has_permission_on :branches, :to => :manage do
406
- if_attribute :managers => contains {user}
407
- end
408
-
409
- has_permission_on :employees, :to => :manage do
410
- if_permitted_to :manage, :branch
411
- # instead of
412
- #if_attribute :branch => {:managers => contains {user}}
413
- end
414
- end
415
- end
416
-
417
- Lastly, not only privileges may be organized in a hierarchy but roles as well.
418
- Here, project manager inherit the permissions of employees.
419
-
420
- role :project_manager do
421
- includes :employee
422
- end
423
-
424
- See also Authorization::Reader.
425
-
426
- == Testing
427
-
428
- declarative_authorization provides a few helpers to ease the testing with
429
- authorization in mind.
430
-
431
- In your test_helper.rb, to enable the helpers add
432
-
433
- require 'declarative_authorization/maintenance'
434
-
435
- class Test::Unit::TestCase
436
- include Authorization::TestHelper
437
- ...
438
- end
439
-
440
- For using the test helpers with RSpec, just add the following lines to your
441
- spec_helper.rb (somewhere after require 'spec/rails'):
442
-
443
- require 'declarative_authorization/maintenance'
444
- include Authorization::TestHelper
445
-
446
- Now, in unit tests, you may deactivate authorization if needed e.g. for test
447
- setup and assume certain identities for tests:
448
-
449
- class EmployeeTest < ActiveSupport::TestCase
450
- def test_should_read
451
- without_access_control do
452
- Employee.create(...)
453
- end
454
- assert_nothing_raised do
455
- with_user(admin) do
456
- Employee.find(:first)
457
- end
458
- end
459
- end
460
- end
461
-
462
- Or, with RSpec, it would work like this:
463
-
464
- describe Employee do
465
- it "should read" do
466
- without_access_control do
467
- Employee.create(...)
468
- end
469
- with_user(admin) do
470
- Employee.find(:first)
471
- end
472
- end
473
- end
474
-
475
- In functional tests, get, posts, etc. may be tested in the name of certain users:
476
-
477
- get_with admin, :index
478
- post_with admin, :update, :employee => {...}
479
-
480
- See Authorization::TestHelper for more information.
481
-
482
-
483
- = Installation of declarative_authorization
484
-
485
- One of three options to install the plugin:
486
- * Install by Gem: Add to your environment.rb in the initializer block:
487
- config.gem "declarative_authorization"
488
- Note: you need gemcutter support in place, i.e. call
489
- gem install gemcutter
490
- gem tumble
491
- And call from your application's root directory
492
- rake gems:install
493
- * Alternatively, in Rails 2, to install from github, execute in your application's root directory
494
- cd vendor/plugins && git clone git://github.com/stffn/declarative_authorization.git
495
-
496
- Then,
497
- * provide the requirements as noted below,
498
- * create a basic config/authorization_rules.rb--you might want to take the
499
- provided example authorization_rules.dist.rb in the plugin root as a starting
500
- point,
501
- * add +filter_access_to+, +permitted_to+? and model security as needed.
502
-
503
- == Providing the Plugin's Requirements
504
- The requirements are
505
- * Rails >= 4.2.5.2 and Ruby >= 2.3.3
506
- * An authentication mechanism
507
- * A user object returned by Controller#current_user
508
- * An array of role symbols returned by User#role_symbols
509
- * (For model security) Setting Authorization.current_user to the request's user
510
-
511
- Of the various ways to provide these requirements, here is one way employing
512
- restful_authentication.
513
-
514
- * Install restful_authentication
515
- cd vendor/plugins && git clone git://github.com/technoweenie/restful-authentication.git restful_authentication
516
- cd ../.. && ruby script/generate authenticated user sessions
517
- * Move "include AuthenticatedSystem" to ApplicationController
518
- * Add +filter_access_to+ calls as described above.
519
- * If you'd like to use model security, add a before_action that sets the user
520
- globally to your ApplicationController. This is thread-safe.
521
- before_action :set_current_user
522
- protected
523
- def set_current_user
524
- Authorization.current_user = current_user
525
- end
526
-
527
- * Add roles field to the User model through a :+has_many+ association
528
- (this is just one possible approach; you could just as easily use
529
- :+has_many+ :+through+ or a serialized roles array):
530
- * create a migration for table roles
531
- class CreateRoles < ActiveRecord::Migration
532
- def self.up
533
- create_table "roles" do |t|
534
- t.column :title, :string
535
- t.references :user
536
- end
537
- end
538
-
539
- def self.down
540
- drop_table "roles"
541
- end
542
- end
543
-
544
- * create a model Role,
545
- class Role < ActiveRecord::Base
546
- belongs_to :user
547
- end
548
-
549
- * add +has_many+ :+roles+ to the User model and a roles method that returns the roles
550
- as an Array of Symbols, e.g.
551
- class User < ActiveRecord::Base
552
- has_many :roles
553
- def role_symbols
554
- (roles || []).map {|r| r.title.to_sym}
555
- end
556
- end
557
-
558
- * add roles to your User objects using e.g.
559
- user.roles.create(:title => "admin")
560
-
561
- Note: If you choose to generate an Account model for restful_authentication
562
- instead of a User model as described above, you have to customize the
563
- examples and create a ApplicationController#current_user method.
564
-
565
-
566
- == Debugging Authorization
567
-
568
- Currently, the main means of debugging authorization decisions is logging and
569
- exceptions. Denied access to actions is logged to +warn+ or +info+, including
570
- some hints about what went wrong.
571
-
572
- All bang methods throw exceptions which may be used to retrieve more
573
- information about a denied access than a Boolean value.
574
-
575
-
576
- = Help and Contact
577
-
578
- We have an issue tracker[http://github.com/appfolio/ae_declarative_authorization/issues]
579
- for bugs and feature requests.
580
- You are very welcome to contribute. Just fork the git repository and send a pull request.
581
-
582
-
583
- = Contributors
584
-
585
- Thanks to John Joseph Bachir, Dennis Blöte, Eike Carls, Damian Caruso, Kai Chen, Erik Dahlstrand,
586
- Jeroen van Dijk, Alexander Dobriakov, Sebastian Dyck, Ari Epstein, Jeremy Friesen,
587
- Tim Harper, John Hawthorn, hollownest, Daniel Kristensen, Jeremy Kleindl, Joel Kociolek,
588
- Benjamin ter Kuile, Brad Langhorst, Brian Langenfeld,
589
- Georg Ledermann, Geoff Longman, Olly Lylo, Mark Mansour, Thomas Maurer, Kevin Moore,
590
- Tyler Pickett, Edward Rudd, Sharagoz,
591
- TJ Singleton, Mike Vincent, Joel Westerberg
592
-
593
-
594
- = License
595
-
596
- Copyright (c) 2008 Steffen Bartsch, TZI, Universität Bremen, Germany
597
- released under the MIT license
data/Rakefile DELETED
@@ -1,35 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler'
3
-
4
- begin
5
- Bundler.setup(:default, :development)
6
- rescue Bundler::BundlerError => e
7
- $stderr.puts e.message
8
- $stderr.puts "Run `bundle install` to install missing gems"
9
- exit e.status_code
10
- end
11
-
12
- require 'rake'
13
- require 'rake/testtask'
14
- require 'rdoc/task'
15
-
16
- require 'bundler/gem_tasks'
17
-
18
- task default: :test
19
-
20
- Rake::TestTask.new(:test) do |t|
21
- t.libs << 'lib' << 'test'
22
- t.pattern = 'test/**/*_test.rb'
23
- t.verbose = true
24
- t.warning = false
25
- end
26
-
27
- Rake::RDocTask.new(:rdoc) do |rdoc|
28
- rdoc.rdoc_dir = 'rdoc'
29
- rdoc.title = 'Authorization'
30
- rdoc.options << '--line-numbers' << '--inline-source'
31
- rdoc.options << '--charset' << 'utf-8'
32
- rdoc.rdoc_files.include('README.rdoc')
33
- rdoc.rdoc_files.include('CHANGELOG')
34
- rdoc.rdoc_files.include('lib/**/*.rb')
35
- end
@@ -1,20 +0,0 @@
1
- authorization do
2
- role :guest do
3
- # add permissions for guests here, e.g.
4
- #has_permission_on :conferences, :to => :read
5
- end
6
-
7
- # permissions on other roles, such as
8
- #role :admin do
9
- # has_permission_on :conferences, :to => :manage
10
- #end
11
- end
12
-
13
- privileges do
14
- # default privilege hierarchies to facilitate RESTful Rails apps
15
- privilege :manage, :includes => [:create, :read, :update, :delete]
16
- privilege :read, :includes => [:index, :show]
17
- privilege :create, :includes => :new
18
- privilege :update, :includes => :edit
19
- privilege :delete, :includes => :destroy
20
- end
data/init.rb DELETED
@@ -1,5 +0,0 @@
1
- begin
2
- require File.join(File.dirname(__FILE__), 'lib', 'declarative_authorization') # From here
3
- rescue LoadError
4
- require 'declarative_authorization' # From gem
5
- end