g5_authenticatable 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +12 -0
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile +2 -2
  5. data/README.md +186 -1
  6. data/app/controllers/concerns/g5_authenticatable/authorization.rb +21 -0
  7. data/app/models/g5_authenticatable/role.rb +8 -0
  8. data/app/models/g5_authenticatable/user.rb +41 -0
  9. data/app/policies/g5_authenticatable/base_policy.rb +73 -0
  10. data/config/initializers/rolify.rb +8 -0
  11. data/g5_authenticatable.gemspec +3 -0
  12. data/lib/g5_authenticatable/engine.rb +3 -0
  13. data/lib/g5_authenticatable/rspec.rb +1 -0
  14. data/lib/g5_authenticatable/test/factory.rb +51 -1
  15. data/lib/g5_authenticatable/test/feature_helpers.rb +15 -2
  16. data/lib/g5_authenticatable/version.rb +1 -1
  17. data/lib/generators/g5_authenticatable/install/USAGE +7 -1
  18. data/lib/generators/g5_authenticatable/install/install_generator.rb +33 -6
  19. data/lib/generators/g5_authenticatable/install/templates/403.html +26 -0
  20. data/lib/generators/g5_authenticatable/install/templates/application_policy.rb +4 -0
  21. data/lib/generators/g5_authenticatable/install/templates/{g5_authenticatable.rb → initializer.rb} +0 -0
  22. data/lib/generators/g5_authenticatable/install/templates/migrate/add_g5_authenticatable_users_contact_info.rb +11 -0
  23. data/lib/generators/g5_authenticatable/install/templates/migrate/create_g5_authenticatable_roles.rb +20 -0
  24. data/lib/generators/g5_authenticatable/install/templates/{create_g5_authenticatable_users.rb → migrate/create_g5_authenticatable_users.rb} +0 -0
  25. data/spec/controllers/application_controller_spec.rb +12 -0
  26. data/spec/controllers/concerns/g5_authenticatable/authorization.rb +50 -0
  27. data/spec/dummy/app/assets/javascripts/posts.js +2 -0
  28. data/spec/dummy/app/assets/stylesheets/posts.css +4 -0
  29. data/spec/dummy/app/assets/stylesheets/scaffold.css +56 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +1 -0
  31. data/spec/dummy/app/controllers/posts_controller.rb +74 -0
  32. data/spec/dummy/app/helpers/posts_helper.rb +2 -0
  33. data/spec/dummy/app/models/post.rb +3 -0
  34. data/spec/dummy/app/policies/application_policy.rb +4 -0
  35. data/spec/dummy/app/policies/post_policy.rb +4 -0
  36. data/spec/dummy/app/views/home/index.html.erb +40 -0
  37. data/spec/dummy/app/views/posts/_form.html.erb +21 -0
  38. data/spec/dummy/app/views/posts/edit.html.erb +6 -0
  39. data/spec/dummy/app/views/posts/index.html.erb +30 -0
  40. data/spec/dummy/app/views/posts/new.html.erb +5 -0
  41. data/spec/dummy/app/views/posts/show.html.erb +17 -0
  42. data/spec/dummy/config/database.yml.ci +1 -2
  43. data/spec/dummy/config/initializers/g5_authenticatable.rb +8 -0
  44. data/spec/dummy/config/routes.rb +2 -0
  45. data/spec/dummy/db/migrate/20150428182339_add_g5_authenticatable_users_contact_info.rb +11 -0
  46. data/spec/dummy/db/migrate/20150429212919_create_g5_authenticatable_roles.rb +20 -0
  47. data/spec/dummy/db/migrate/20150509061150_create_posts.rb +9 -0
  48. data/spec/dummy/db/schema.rb +37 -4
  49. data/spec/dummy/public/403.html +26 -0
  50. data/spec/factories/post.rb +6 -0
  51. data/spec/features/default_role_authorization_spec.rb +254 -0
  52. data/spec/features/sign_in_spec.rb +144 -8
  53. data/spec/lib/generators/g5_authenticatable/install_generator_spec.rb +72 -1
  54. data/spec/models/g5_authenticatable/role_spec.rb +81 -0
  55. data/spec/models/g5_authenticatable/user_spec.rb +340 -3
  56. data/spec/models/post_spec.rb +12 -0
  57. data/spec/policies/application_policy_spec.rb +171 -0
  58. data/spec/policies/post_policy_spec.rb +35 -0
  59. data/spec/requests/default_role_authorization_spec.rb +169 -0
  60. data/spec/spec_helper.rb +0 -3
  61. data/spec/support/shared_examples/super_admin_authorizer.rb +33 -0
  62. metadata +109 -5
  63. data/circle.yml +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7098ea50ca9cdaa0ffefa42a0fe47d45fb087b1
4
- data.tar.gz: ff8f79d3caa16d549db587bd9b01db5038e701a1
3
+ metadata.gz: 636e93c7a2f6114bba1839103deb438f1d713098
4
+ data.tar.gz: b5d0f845a01f7a80521650e1604254940ca35992
5
5
  SHA512:
6
- metadata.gz: 437258a28abe7308db839bf6c2c64257b45bcf7f74fae81d5502dc46fb414ba9dd1ca81c2f240b753f87a496c833523e405db27d589944072585b79b8c913869
7
- data.tar.gz: f9747b13d67c6559d8df0e74498a62fc2cd3722d59e6eeb55ecfba1bde51a36601930b30885f6f0baddd5bbbf892bcbfc62ba9e65632f97e97fc380f27b19013
6
+ metadata.gz: d934f79a4045ecc1b3ca42a81e36582ef0ede8d1c23250783d7c128683c4da1b4c76aab81c19ef890b78d4a321326d0cfba1735dbd86243f32e62b05a3f78ad0
7
+ data.tar.gz: 465efb38e1da5eb37bec731666840d1b36245dd168f63299a1f297024a6a4cab6d88c367f2e7966b9096eed6c48f07dcacf5c66d0558428036161c590cf825b0
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ script:
5
+ - RAILS_ENV=test bundle exec rake app:db:setup
6
+ - bundle exec rspec spec
7
+ before_script:
8
+ - cp spec/dummy/config/database.yml.ci spec/dummy/config/database.yml
9
+ - psql -c 'create database g5_authenticatable_test;' -U postgres
10
+ env:
11
+ global:
12
+ - DEVISE_SECRET_KEY=foo
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## v0.5.0 (2015-05-21)
2
+
3
+ * Added user roles. Requires running `rails g5 g5_authenticatable:install` and
4
+ `rake db:migrate`
5
+ ([#33](https://github.com/G5/g5_authenticatable/pull/33))
6
+ * Added user attributes. Requires running `rails g g5_authenticatable:install`
7
+ and `rake db:migrate`
8
+ ([#32](https://github.com/G5/g5_authenticatable/pull/32))
9
+ * Updated documentation around test helper dependencies and incompatibilities
10
+ ([#30](https://github.com/G5/g5_authenticatable/pull/30))
11
+
1
12
  ## v0.4.2 (2015-02-10)
2
13
 
3
14
  * Override `Devise::FailureApp` with fix for compatibility with Rails 4.2
data/Gemfile CHANGED
@@ -12,8 +12,8 @@ gem 'pg'
12
12
  gem 'grape'
13
13
 
14
14
  group :test, :development do
15
- gem 'rspec-rails', '~> 2.99'
16
- gem 'pry'
15
+ gem 'rspec-rails', '~> 3.1'
16
+ gem 'pry-byebug'
17
17
  gem 'dotenv-rails'
18
18
  gem 'web-console', '~> 2.0'
19
19
  end
data/README.md CHANGED
@@ -18,7 +18,7 @@ library in isolation.
18
18
 
19
19
  ## Current Version
20
20
 
21
- 0.4.2
21
+ 0.5.0
22
22
 
23
23
  ## Requirements
24
24
 
@@ -258,12 +258,197 @@ Authorization header, or in a request parameter named `access_token`.
258
258
  For more details, see the documentation for
259
259
  [g5_authenticatable_api](https://github.com/G5/g5_authenticatable_api).
260
260
 
261
+ ### Authorization ###
262
+
263
+ #### User Roles ####
264
+
265
+ User roles are defined on the auth server and automatically populated in the local
266
+ model layer when a user logs in:
267
+
268
+ ```ruby
269
+ current_user.roles
270
+ # => #<ActiveRecord::Associations::CollectionProxy [#<G5Authenticatable::Role id: 1, name: "viewer", ...>]>
271
+ ```
272
+
273
+ We use [rolify](https://github.com/RolifyCommunity/rolify) for role management,
274
+ which provides an interface for querying role assignments:
275
+
276
+ ```ruby
277
+ current_user.has_role?(:editor)
278
+ ```
279
+
280
+ G5 currently supports four different roles: `:super_admin`, `:admin`,
281
+ `:editor`, and `:viewer` (the default role).
282
+
283
+ #### Policies and Scopes ####
284
+
285
+ G5 Authenticatable uses [pundit](https://github.com/elabs/pundit) to encapsulate
286
+ the authorization logic in policy objects. The pundit documentation contains a much
287
+ more thorough explanation of how to define and use policies, but a quick overview
288
+ is provided here.
289
+
290
+ The G5 Authenticatable generator created an `app/policies/application_policy.rb`
291
+ file in your project:
292
+
293
+ ```ruby
294
+ class ApplicationPolicy < G5Authenticatable::BasePolicy
295
+ class Scope < BaseScope
296
+ end
297
+ end
298
+ ```
299
+
300
+ The `G5Authenticatable::BasePolicy` and `G5Authenticatable::BasePolicy::BaseScope`
301
+ implement a set of default authorization rules that essentially forbids access
302
+ to all actions on all model instances unless the user has the `:super_admin`
303
+ role. It also provides a set of helper methods for checking user roles:
304
+ `super_admin?`, `admin?`, `editor?`, or `viewer?`.
305
+
306
+ In order to implement a custom policy for one of your application's models, you
307
+ can create a new policy in the `app/policies` directory. For instance, if you
308
+ have a `Widget` model, and you want to also grant permissions to update that
309
+ model to users with `:admin` or `:editor` roles:
310
+
311
+ ```ruby
312
+ # app/policies/widget_policy.rb
313
+
314
+ class WidgetPolicy < ApplicationPolicy
315
+ def update?
316
+ super_admin? || admin? || editor?
317
+ end
318
+ end
319
+ ```
320
+
321
+ You also have access to the record being authorized, and can define rules based
322
+ on that. For instance, if you want to restrict `Widget` deletion based on both
323
+ user role and some flag on the `Widget` instance to be deleted:
324
+
325
+ ```ruby
326
+ # app/policies/widget_policy.rb
327
+
328
+ class WidgetPolicy < ApplicationPolicy
329
+ def destroy?
330
+ (super_admin? || admin?) && !record.published?
331
+ end
332
+ end
333
+ ```
334
+
335
+ In order to implement data-level authorization, you can define a custom scope
336
+ within your policy. The scope `resolve` method should only return the records
337
+ to which the current user has access. You have access to the current `user` and
338
+ also the `scope` object (defaults to the record class). For instance, if a user
339
+ must be the owner of a widget in order to access it, but super admins are allowed
340
+ to access all widgets:
341
+
342
+ ```ruby
343
+ # app/policies/widget_policy.rb
344
+
345
+ class WidgetPolicy < ApplicationPolicy
346
+ class Scope < Scope
347
+ def resolve
348
+ if user.has_role?(:super_admin?)
349
+ scope.all
350
+ else
351
+ scope.where(owner_id: user.id)
352
+ end
353
+ end
354
+ end
355
+ end
356
+ ```
357
+
358
+ If you want to apply the same authorization logic across all models in your
359
+ application, you can define them in `ApplicationPolicy` or
360
+ `ApplicationPolicy::Scope`.
361
+
362
+ #### Using Policies in Controllers ####
363
+
364
+ You can use the `authorize` method in your controller to ensure that the
365
+ current user has access to the current action on a particular model instance.
366
+ For instance, in your controller:
367
+
368
+ ```ruby
369
+ class WidgetsController < ApplicationController
370
+ # ...
371
+
372
+ def update
373
+ @widget = Widget.find(params[:id])
374
+ authorize(@widget)
375
+
376
+ if @widget.update_attributes(widget_params)
377
+ flash[:notice] = "Widget successfully updated."
378
+ end
379
+
380
+ respond_with(@widget)
381
+ end
382
+ end
383
+ ```
384
+
385
+ In this example, the `authorize` method will automatically lookup the correct policy
386
+ and populate it with the `current_user` and the model argument, and then call the
387
+ `update?` method on it, based on the current action name.
388
+
389
+ You can directly access the policy scope to look up the records to which the
390
+ current user has access by using the `policy_scope` method:
391
+
392
+ ```ruby
393
+ class WidgetsController < ApplicationController
394
+ # ...
395
+
396
+ def index
397
+ @widgets = policy_scope(Widget)
398
+ end
399
+ ```
400
+
401
+ In this example, the `policy_scope` method will automatically look up the
402
+ `WidgetPolicy::Scope`, populate it with the current user and `Widget` record
403
+ class, and call the scope `resolve` method to retrieve the results.
404
+
405
+ #### Using Policies in Views ####
406
+
407
+ Sometimes, you want to be able to access authorization logic inside Rails views.
408
+ For instance, you may want to hide the link to edit a record if the user does
409
+ not have access to update that record. In these cases, you can use the `policy`
410
+ method to lookup the instance of the policy directly:
411
+
412
+ ```erb
413
+ <% if policy(@widget).update? %>
414
+ <%= link_to 'Edit', edit_widget_path(@widget) %>
415
+ <% end %>
416
+ ```
417
+
418
+ You can also call the `policy_scope` method inside views.
419
+
420
+ If you are not rendering your views within your Rails application (for
421
+ instance, if you are using an Ember front-end), then you will have to build
422
+ your own API for querying policy information in order to access this
423
+ functionality directly in your view.
424
+
261
425
  ### Test Helpers ###
262
426
 
263
427
  G5 Authenticatable currently only supports [rspec-rails](https://github.com/rspec/rspec-rails).
264
428
  Helpers and shared contexts are provided for integration testing secure pages
265
429
  and API methods.
266
430
 
431
+ #### Prerequisites ####
432
+
433
+ Because the test helpers are optional, bundler will not automatically install
434
+ these dependencies. You will have to add the following gems to your own Gemfile:
435
+
436
+ * [rspec-rails](https://github.com/rspec/rspec-rails)
437
+ * [factory_girl_rails](https://github.com/thoughtbot/factory_girl_rails)
438
+ * [webmock](https://github.com/bblimke/webmock)
439
+
440
+ #### Incompatibilities ####
441
+
442
+ There are [known issues](https://github.com/G5/g5_authenticatable/issues/27) using the
443
+ auth test helpers with [selenium-webdriver](http://docs.seleniumhq.org/projects/webdriver/)
444
+ and the Firefox browser. As such, selenium-webdriver is not officially supported by
445
+ the G5 Authenticatable library.
446
+
447
+ If you are using [capybara](https://github.com/jnicklas/capybara) to run your
448
+ integration tests, we highly recommend using
449
+ [poltergeist](https://github.com/teampoltergeist/poltergeist) for PhantomJS as
450
+ your javascript driver instead.
451
+
267
452
  #### Installation ####
268
453
 
269
454
  To automatically mix in helpers to your feature and request specs, include the
@@ -0,0 +1,21 @@
1
+ module G5Authenticatable
2
+ module Authorization
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Pundit
7
+ rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
8
+ end
9
+
10
+ def user_not_authorized
11
+ respond_to do |format|
12
+ format.json do
13
+ render status: :forbidden, json: {error: 'Access forbidden'}
14
+ end
15
+ format.html do
16
+ render status: :forbidden, file: "#{Rails.root}/public/403"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ module G5Authenticatable
2
+ class Role < ActiveRecord::Base
3
+ has_and_belongs_to_many :users, :join_table => :g5_authenticatable_users_roles
4
+ belongs_to :resource, :polymorphic => true
5
+
6
+ scopify
7
+ end
8
+ end
@@ -1,8 +1,49 @@
1
1
  module G5Authenticatable
2
2
  class User < ActiveRecord::Base
3
3
  devise :g5_authenticatable, :trackable, :timeoutable
4
+ rolify role_cname: 'G5Authenticatable::Role',
5
+ role_join_table_name: :g5_authenticatable_users_roles
4
6
 
5
7
  validates :email, presence: true, uniqueness: true
6
8
  validates_uniqueness_of :uid, scope: :provider
9
+
10
+ def self.new_with_session(params, session)
11
+ user = super(params, session)
12
+ auth_data = session['omniauth.auth']
13
+
14
+ if auth_data
15
+ user.assign_attributes(extended_auth_attributes(auth_data))
16
+ user.update_roles_from_auth(auth_data)
17
+ end
18
+
19
+ user
20
+ end
21
+
22
+ def self.find_and_update_for_g5_oauth(auth_data)
23
+ user = super(auth_data)
24
+
25
+ if user
26
+ user.update_attributes(extended_auth_attributes(auth_data))
27
+ user.update_roles_from_auth(auth_data)
28
+ end
29
+
30
+ user
31
+ end
32
+
33
+ def update_roles_from_auth(auth_data)
34
+ roles.clear
35
+ auth_data.extra.roles.each { |role| add_role(role.name) }
36
+ end
37
+
38
+ private
39
+ def self.extended_auth_attributes(auth_data)
40
+ {
41
+ first_name: auth_data.info.first_name,
42
+ last_name: auth_data.info.last_name,
43
+ phone_number: auth_data.info.phone,
44
+ title: auth_data.extra.title,
45
+ organization_name: auth_data.extra.organization_name
46
+ }
47
+ end
7
48
  end
8
49
  end
@@ -0,0 +1,73 @@
1
+ class G5Authenticatable::BasePolicy
2
+ attr_reader :user, :record
3
+
4
+ def initialize(user, record)
5
+ @user = user
6
+ @record = record
7
+ end
8
+
9
+ def index?
10
+ super_admin?
11
+ end
12
+
13
+ def show?
14
+ scope.where(:id => record.id).exists?
15
+ end
16
+
17
+ def create?
18
+ super_admin?
19
+ end
20
+
21
+ def new?
22
+ create?
23
+ end
24
+
25
+ def update?
26
+ super_admin?
27
+ end
28
+
29
+ def edit?
30
+ update?
31
+ end
32
+
33
+ def destroy?
34
+ super_admin?
35
+ end
36
+
37
+ def scope
38
+ Pundit.policy_scope!(user, record.class)
39
+ end
40
+
41
+ class BaseScope
42
+ attr_reader :user, :scope
43
+
44
+ def initialize(user, scope)
45
+ @user = user
46
+ @scope = scope
47
+ end
48
+
49
+ def resolve
50
+ if user.has_role?(:super_admin)
51
+ scope.all
52
+ else
53
+ scope.none
54
+ end
55
+ end
56
+ end
57
+
58
+ def super_admin?
59
+ user.present? && user.has_role?(:super_admin)
60
+ end
61
+
62
+ def admin?
63
+ user.present? && user.has_role?(:admin)
64
+ end
65
+
66
+ def editor?
67
+ user.present? && user.has_role?(:editor)
68
+ end
69
+
70
+ def viewer?
71
+ user.present? && user.has_role?(:viewer)
72
+ end
73
+ end
@@ -0,0 +1,8 @@
1
+ Rolify.configure('G5Authenticatable::Role') do |config|
2
+ # By default ORM adapter is ActiveRecord. uncomment to use mongoid
3
+ # config.use_mongoid
4
+
5
+ # Dynamic shortcuts for User class (user.is_admin? like methods). Default is: false
6
+ # Enable this feature _after_ running rake db:migrate as it relies on the roles table
7
+ # config.use_dynamic_shortcuts
8
+ end
@@ -21,5 +21,8 @@ Gem::Specification.new do |spec|
21
21
  spec.require_paths = ['lib']
22
22
 
23
23
  spec.add_dependency 'devise_g5_authenticatable', '~> 0.2'
24
+ spec.add_dependency 'omniauth-g5', '~> 0.2'
24
25
  spec.add_dependency 'g5_authenticatable_api', '~> 0.3.1'
26
+ spec.add_dependency 'rolify', '~> 4.0'
27
+ spec.add_dependency 'pundit', '~> 1.0'
25
28
  end
@@ -1,3 +1,6 @@
1
+ require 'rolify'
2
+ require 'pundit'
3
+
1
4
  module G5Authenticatable
2
5
  class Engine < ::Rails::Engine
3
6
  isolate_namespace G5Authenticatable
@@ -4,3 +4,4 @@ require 'g5_authenticatable/test/token_validation_helpers'
4
4
  require 'g5_authenticatable/test/feature_helpers'
5
5
  require 'g5_authenticatable/test/request_helpers'
6
6
  require 'g5_authenticatable/test/controller_helpers'
7
+ require 'pundit/rspec'
@@ -1,10 +1,60 @@
1
1
  require 'factory_girl_rails'
2
2
 
3
3
  FactoryGirl.define do
4
- factory :g5_authenticatable_user, :class => 'G5Authenticatable::User' do
4
+ factory :g5_authenticatable_user, class: 'G5Authenticatable::User' do
5
5
  sequence(:email) { |n| "test.user#{n}@test.host" }
6
6
  provider 'g5'
7
7
  sequence(:uid) { |n| "abc123-#{n}" }
8
8
  sequence(:g5_access_token) { |n| "secret_token_#{n}" }
9
+ first_name 'Jane'
10
+ last_name 'Doe'
11
+ phone_number '(555) 867-5309'
12
+ title 'Minister of Funny Walks'
13
+ organization_name 'Department of Redundancy Department'
14
+
15
+ after(:build) do |user|
16
+ user.roles << FactoryGirl.build(:g5_authenticatable_viewer_role)
17
+ end
18
+ end
19
+
20
+ factory :g5_authenticatable_super_admin, parent: :g5_authenticatable_user do
21
+ after(:build) do |user|
22
+ user.roles.clear
23
+ user.roles << FactoryGirl.build(:g5_authenticatable_super_admin_role)
24
+ end
25
+ end
26
+
27
+ factory :g5_authenticatable_admin, parent: :g5_authenticatable_user do
28
+ after(:build) do |user|
29
+ user.roles.clear
30
+ user.roles << FactoryGirl.build(:g5_authenticatable_admin_role)
31
+ end
32
+ end
33
+
34
+ factory :g5_authenticatable_editor, parent: :g5_authenticatable_user do
35
+ after(:build) do |user|
36
+ user.roles.clear
37
+ user.roles << FactoryGirl.build(:g5_authenticatable_editor_role)
38
+ end
39
+ end
40
+
41
+ factory :g5_authenticatable_role, class: 'G5Authenticatable::Role' do
42
+ sequence(:name) { |n| "role_#{n}" }
43
+ end
44
+
45
+ factory :g5_authenticatable_super_admin_role, parent: :g5_authenticatable_role do
46
+ name 'super_admin'
47
+ end
48
+
49
+ factory :g5_authenticatable_admin_role, parent: :g5_authenticatable_role do
50
+ name 'admin'
51
+ end
52
+
53
+ factory :g5_authenticatable_editor_role, parent: :g5_authenticatable_role do
54
+ name 'editor'
55
+ end
56
+
57
+ factory :g5_authenticatable_viewer_role, parent: :g5_authenticatable_role do
58
+ name 'viewer'
9
59
  end
10
60
  end
@@ -5,8 +5,21 @@ module G5Authenticatable
5
5
  OmniAuth.config.mock_auth[:g5] = OmniAuth::AuthHash.new({
6
6
  uid: user.uid,
7
7
  provider: 'g5',
8
- info: {email: user.email},
9
- credentials: {token: user.g5_access_token}
8
+ info: {
9
+ email: user.email,
10
+ first_name: user.first_name,
11
+ last_name: user.last_name,
12
+ phone: user.phone_number
13
+ },
14
+ credentials: {token: user.g5_access_token},
15
+ extra: {
16
+ title: user.title,
17
+ organization_name: user.organization_name,
18
+ roles: user.roles.collect do |role|
19
+ {name: role.name}
20
+ end,
21
+ raw_info: {}
22
+ }
10
23
  }.merge(options))
11
24
  end
12
25
 
@@ -1,3 +1,3 @@
1
1
  module G5Authenticatable
2
- VERSION = '0.4.2'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -1,11 +1,17 @@
1
1
  Description:
2
- Installs the g5_authenticatable engine
2
+ Installs/updates the g5_authenticatable engine
3
3
 
4
4
  Example:
5
5
  rails generate g5_authenticatable:install
6
6
 
7
7
  This will create:
8
+ app/policies/application_policy.rb
9
+ config/initializers/g5_authenticatable.rb
8
10
  db/migrate/create_g5_authenticatable_users.rb
11
+ db/migrate/add_g5_authenticatable_users_contact_info.rb
12
+ db/migrate/create_g5_authenticatable_roles.rb
13
+ public/403.html
9
14
 
10
15
  This will modify:
16
+ app/controllers/application_controller.rb
11
17
  config/routes.rb
@@ -9,16 +9,43 @@ class G5Authenticatable::InstallGenerator < Rails::Generators::Base
9
9
  ActiveRecord::Migration.next_migration_number(next_migration_number)
10
10
  end
11
11
 
12
- def create_users_migration
13
- filename = 'create_g5_authenticatable_users.rb'
14
- migration_template filename, "db/migrate/#{filename}"
15
- end
16
-
17
12
  def mount_engine
18
13
  route "mount G5Authenticatable::Engine => '/g5_auth'"
19
14
  end
20
15
 
21
16
  def create_initializer
22
- template 'g5_authenticatable.rb', 'config/initializers/g5_authenticatable.rb'
17
+ template 'initializer.rb', 'config/initializers/g5_authenticatable.rb'
18
+ end
19
+
20
+ def create_users_migration
21
+ copy_migration('create_g5_authenticatable_users')
22
+ end
23
+
24
+ def users_contact_info_migration
25
+ copy_migration('add_g5_authenticatable_users_contact_info')
26
+ end
27
+
28
+ def create_roles_migration
29
+ copy_migration('create_g5_authenticatable_roles')
30
+ end
31
+
32
+ def include_authorization
33
+ inject_into_file 'app/controllers/application_controller.rb',
34
+ after: "class ApplicationController < ActionController::Base\n" do
35
+ " include G5Authenticatable::Authorization\n"
36
+ end
37
+ end
38
+
39
+ def create_application_policy
40
+ template 'application_policy.rb', 'app/policies/application_policy.rb'
41
+ end
42
+
43
+ def create_403_error_page
44
+ template '403.html', 'public/403.html'
45
+ end
46
+
47
+ private
48
+ def copy_migration(name)
49
+ migration_template "migrate/#{name}.rb", "db/migrate/#{name}.rb"
23
50
  end
24
51
  end
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Access forbidden (403)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/403.html -->
21
+ <div class="dialog">
22
+ <h1>Access forbidden</h1>
23
+ <p>You do not have permission to access this page.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,4 @@
1
+ class ApplicationPolicy < G5Authenticatable::BasePolicy
2
+ class Scope < BaseScope
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ class AddG5AuthenticatableUsersContactInfo < ActiveRecord::Migration
2
+ def change
3
+ change_table(:g5_authenticatable_users) do |t|
4
+ t.string :first_name
5
+ t.string :last_name
6
+ t.string :phone_number
7
+ t.string :title
8
+ t.string :organization_name
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ class CreateG5AuthenticatableRoles < ActiveRecord::Migration
2
+ def change
3
+ create_table(:g5_authenticatable_roles) do |t|
4
+ t.string :name
5
+ t.references :resource, :polymorphic => true
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ create_table(:g5_authenticatable_users_roles, :id => false) do |t|
11
+ t.references :user
12
+ t.references :role
13
+ end
14
+
15
+ add_index(:g5_authenticatable_roles, :name)
16
+ add_index(:g5_authenticatable_roles, [ :name, :resource_type, :resource_id ],
17
+ name: 'index_g5_authenticatable_roles_on_name_and_resource')
18
+ add_index(:g5_authenticatable_users_roles, [ :user_id, :role_id ])
19
+ end
20
+ end