g5_authenticatable 0.4.2 → 0.5.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.
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