g5_authenticatable 0.3.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 (94) hide show
  1. checksums.yaml +7 -0
  2. data/.env +1 -0
  3. data/.gitignore +25 -0
  4. data/.rspec +1 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +45 -0
  7. data/Gemfile +37 -0
  8. data/LICENSE +22 -0
  9. data/README.md +519 -0
  10. data/Rakefile +20 -0
  11. data/app/assets/images/g5_authenticatable/.gitkeep +0 -0
  12. data/app/assets/javascripts/g5_authenticatable/application.js +15 -0
  13. data/app/assets/stylesheets/g5_authenticatable/application.css +13 -0
  14. data/app/controllers/g5_authenticatable/application_controller.rb +4 -0
  15. data/app/controllers/g5_authenticatable/error_controller.rb +9 -0
  16. data/app/controllers/g5_authenticatable/sessions_controller.rb +23 -0
  17. data/app/helpers/g5_authenticatable/application_helper.rb +4 -0
  18. data/app/models/g5_authenticatable/user.rb +8 -0
  19. data/app/views/g5_authenticatable/error/auth_error.html.erb +1 -0
  20. data/app/views/layouts/g5_authenticatable/application.html.erb +20 -0
  21. data/circle.yml +4 -0
  22. data/config/initializers/devise.rb +257 -0
  23. data/config/locales/devise.en.yml +59 -0
  24. data/config/routes.rb +6 -0
  25. data/g5_authenticatable.gemspec +25 -0
  26. data/lib/g5_authenticatable.rb +7 -0
  27. data/lib/g5_authenticatable/engine.rb +15 -0
  28. data/lib/g5_authenticatable/rspec.rb +4 -0
  29. data/lib/g5_authenticatable/test/controller_helpers.rb +59 -0
  30. data/lib/g5_authenticatable/test/factory.rb +10 -0
  31. data/lib/g5_authenticatable/test/feature_helpers.rb +38 -0
  32. data/lib/g5_authenticatable/test/request_helpers.rb +29 -0
  33. data/lib/g5_authenticatable/version.rb +3 -0
  34. data/lib/generators/g5_authenticatable/install/USAGE +11 -0
  35. data/lib/generators/g5_authenticatable/install/install_generator.rb +20 -0
  36. data/lib/generators/g5_authenticatable/install/templates/create_g5_authenticatable_users.rb +23 -0
  37. data/lib/tasks/g5_authenticatable_tasks.rake +4 -0
  38. data/script/rails +8 -0
  39. data/spec/config/application_spec.rb +7 -0
  40. data/spec/controllers/.gitkeep +0 -0
  41. data/spec/controllers/application_controller_spec.rb +22 -0
  42. data/spec/dummy/README.rdoc +261 -0
  43. data/spec/dummy/Rakefile +7 -0
  44. data/spec/dummy/app/api/secure_api.rb +8 -0
  45. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  46. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  47. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  48. data/spec/dummy/app/controllers/home_controller.rb +9 -0
  49. data/spec/dummy/app/controllers/rails_api/secure_resources_controller.rb +17 -0
  50. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  51. data/spec/dummy/app/mailers/.gitkeep +0 -0
  52. data/spec/dummy/app/models/.gitkeep +0 -0
  53. data/spec/dummy/app/views/home/index.html.erb +1 -0
  54. data/spec/dummy/app/views/home/show.html.erb +1 -0
  55. data/spec/dummy/app/views/layouts/application.html.erb +16 -0
  56. data/spec/dummy/app/views/rails_api/secure_resources/show.html.erb +0 -0
  57. data/spec/dummy/config.ru +4 -0
  58. data/spec/dummy/config/application.rb +63 -0
  59. data/spec/dummy/config/boot.rb +10 -0
  60. data/spec/dummy/config/database.yml.ci +6 -0
  61. data/spec/dummy/config/database.yml.sample +13 -0
  62. data/spec/dummy/config/environment.rb +5 -0
  63. data/spec/dummy/config/environments/development.rb +29 -0
  64. data/spec/dummy/config/environments/production.rb +65 -0
  65. data/spec/dummy/config/environments/test.rb +33 -0
  66. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  67. data/spec/dummy/config/initializers/inflections.rb +15 -0
  68. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  69. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  70. data/spec/dummy/config/initializers/session_store.rb +8 -0
  71. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  72. data/spec/dummy/config/locales/en.yml +5 -0
  73. data/spec/dummy/config/routes.rb +15 -0
  74. data/spec/dummy/db/migrate/20140206070137_create_g5_authenticatable_users.rb +23 -0
  75. data/spec/dummy/db/schema.rb +33 -0
  76. data/spec/dummy/lib/assets/.gitkeep +0 -0
  77. data/spec/dummy/log/.gitkeep +0 -0
  78. data/spec/dummy/public/404.html +26 -0
  79. data/spec/dummy/public/422.html +26 -0
  80. data/spec/dummy/public/500.html +25 -0
  81. data/spec/dummy/public/favicon.ico +0 -0
  82. data/spec/dummy/script/rails +6 -0
  83. data/spec/features/auth_error_path_spec.rb +21 -0
  84. data/spec/features/sign_in_spec.rb +68 -0
  85. data/spec/g5_authenticatable/version_spec.rb +7 -0
  86. data/spec/lib/generators/g5_authenticatable/install_generator_spec.rb +55 -0
  87. data/spec/models/.gitkeep +0 -0
  88. data/spec/models/g5_authenticatable/user_spec.rb +39 -0
  89. data/spec/requests/grape_api_spec.rb +19 -0
  90. data/spec/requests/rails_api_spec.rb +53 -0
  91. data/spec/routing/auth_error_routing_spec.rb +15 -0
  92. data/spec/spec_helper.rb +52 -0
  93. data/spec/support/devise.rb +3 -0
  94. metadata +222 -0
@@ -0,0 +1,15 @@
1
+ Rails.application.routes.draw do
2
+ resource :home, only: [:index, :show]
3
+
4
+ get '/protected_page', to: 'home#show', as: :protected_page
5
+
6
+ namespace :rails_api do
7
+ resource :secure_resource, only: [:create, :show],
8
+ defaults: {format: 'json'}
9
+ end
10
+
11
+ mount G5Authenticatable::Engine => '/g5_auth'
12
+ mount SecureApi => '/api'
13
+
14
+ root to: 'home#index'
15
+ end
@@ -0,0 +1,23 @@
1
+ class CreateG5AuthenticatableUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table(:g5_authenticatable_users) do |t|
4
+ # G5 authenticatable
5
+ t.string :email, null: false, default: ''
6
+ t.string :provider, null: false, default: 'g5'
7
+ t.string :uid, null: false
8
+ t.string :g5_access_token
9
+
10
+ # Trackable
11
+ t.integer :sign_in_count, default: 0, null: false
12
+ t.datetime :current_sign_in_at
13
+ t.datetime :last_sign_in_at
14
+ t.string :current_sign_in_ip
15
+ t.string :last_sign_in_ip
16
+
17
+ t.timestamps
18
+ end
19
+
20
+ add_index :g5_authenticatable_users, :email, unique: true
21
+ add_index :g5_authenticatable_users, [:provider, :uid], unique: true
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 20140206070137) do
15
+
16
+ create_table "g5_authenticatable_users", force: true do |t|
17
+ t.string "email", default: "", null: false
18
+ t.string "provider", default: "g5", null: false
19
+ t.string "uid", null: false
20
+ t.string "g5_access_token"
21
+ t.integer "sign_in_count", default: 0, null: false
22
+ t.datetime "current_sign_in_at"
23
+ t.datetime "last_sign_in_at"
24
+ t.string "current_sign_in_ip"
25
+ t.string "last_sign_in_ip"
26
+ t.datetime "created_at"
27
+ t.datetime "updated_at"
28
+ end
29
+
30
+ add_index "g5_authenticatable_users", ["email"], name: "index_g5_authenticatable_users_on_email", unique: true
31
+ add_index "g5_authenticatable_users", ["provider", "uid"], name: "index_g5_authenticatable_users_on_provider_and_uid", unique: true
32
+
33
+ end
File without changes
File without changes
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</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/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</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/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</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/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'error path' do
4
+
5
+ context 'when there is an error at the oauth server' do
6
+
7
+ before do
8
+ stub_g5_invalid_credentials
9
+ end
10
+
11
+ it 'should redirect to the error path' do
12
+ visit(protected_page_path)
13
+ expect(current_path).to eq('/g5_auth/auth_error')
14
+ end
15
+
16
+ it 'should not use the application layout' do
17
+ visit(protected_page_path)
18
+ expect(page).to_not have_title('Dummy')
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Signing in' do
4
+ let(:user) { FactoryGirl.create(:g5_authenticatable_user) }
5
+
6
+ context 'from a login link' do
7
+ subject(:login) { click_link 'Login' }
8
+
9
+ before { visit root_path }
10
+
11
+ context 'when user exists locally' do
12
+ before do
13
+ stub_g5_omniauth(user)
14
+ login
15
+ end
16
+
17
+ it 'should sign in the user successfully' do
18
+ expect(page).to have_content('Signed in successfully.')
19
+ end
20
+
21
+ it 'should redirect the user to the root path' do
22
+ expect(current_path).to eq(root_path)
23
+ end
24
+ end
25
+
26
+ context 'when user does not exist locally' do
27
+ before do
28
+ OmniAuth.config.mock_auth[:g5] = OmniAuth::AuthHash.new({
29
+ uid: uid,
30
+ provider: 'g5',
31
+ info: {email: 'new.test.user@test.host'},
32
+ credentials: {token: g5_access_token}
33
+ })
34
+ end
35
+
36
+ let(:uid) { 42 }
37
+ let(:g5_access_token) { 'my secret token string' }
38
+
39
+ it 'should sign in the user successfully' do
40
+ login
41
+ expect(page).to have_content('Signed in successfully.')
42
+ end
43
+
44
+ it 'should redirect the user to the root path' do
45
+ login
46
+ expect(current_path).to eq(root_path)
47
+ end
48
+
49
+ it 'should create the user locally' do
50
+ expect { login }.to change { G5Authenticatable::User.count }.by(1)
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'when visiting a protected page' do
56
+ before do
57
+ visit_path_and_login_with(protected_page_path, user)
58
+ end
59
+
60
+ it 'should sign in the user successfully' do
61
+ expect(page).to have_content('Signed in successfully.')
62
+ end
63
+
64
+ it 'should redirect the user to the protected page' do
65
+ expect(current_path).to eq(protected_page_path)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe G5Authenticatable do
4
+ it 'should have a version' do
5
+ expect(G5Authenticatable::VERSION).to_not be_blank
6
+ end
7
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ # For some reason, trying to load the generator from this spec
4
+ # causes problems without an explicit require statement, even
5
+ # though the rails executable is able to find the generator
6
+ # when you execute it from the command line
7
+ require 'generators/g5_authenticatable/install/install_generator'
8
+
9
+ describe G5Authenticatable::InstallGenerator, type: :generator do
10
+ destination File.expand_path('../../../tmp', __FILE__)
11
+
12
+ before do
13
+ prepare_destination
14
+ setup_routes
15
+ run_generator
16
+ end
17
+
18
+ it 'should copy the migration' do
19
+ expect(destination_root).to have_structure {
20
+ directory 'db' do
21
+ directory 'migrate' do
22
+ migration 'create_g5_authenticatable_users' do
23
+ contains 'class CreateG5AuthenticatableUsers < ActiveRecord::Migration'
24
+ end
25
+ end
26
+ end
27
+ }
28
+ end
29
+
30
+ it 'should mount the engine' do
31
+ expect(destination_root).to have_structure {
32
+ directory 'config' do
33
+ file 'routes.rb' do
34
+ contains "mount G5Authenticatable::Engine => '/g5_auth'"
35
+ end
36
+ end
37
+ }
38
+ end
39
+
40
+ def setup_routes
41
+ routes = <<-END
42
+ Rails.application.routes.draw do
43
+ resource :home, only: [:show, :index]
44
+
45
+ match '/some_path', to: 'controller#action', as: :my_alias
46
+
47
+ root to: 'home#index'
48
+ end
49
+ END
50
+ config_dir = File.join(destination_root, 'config')
51
+
52
+ FileUtils.mkdir_p(config_dir)
53
+ File.write(File.join(config_dir, 'routes.rb'), routes)
54
+ end
55
+ end
File without changes
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe G5Authenticatable::User do
4
+ subject { user }
5
+ let(:user) { G5Authenticatable::User.create(user_attributes) }
6
+ let(:user_attributes) { FactoryGirl.attributes_for(:g5_authenticatable_user) }
7
+
8
+ it 'should expose the email' do
9
+ expect(user.email).to eq(user_attributes[:email])
10
+ end
11
+
12
+ it 'should expose the user provider' do
13
+ expect(user.provider).to eq(user_attributes[:provider])
14
+ end
15
+
16
+ it 'should expose the user uid' do
17
+ expect(user.uid).to eq(user_attributes[:uid])
18
+ end
19
+
20
+ it 'should expose a g5_access_token' do
21
+ expect(user.g5_access_token).to eq(user_attributes[:g5_access_token])
22
+ end
23
+
24
+ it 'should initialize the sign_in_count' do
25
+ expect(user.sign_in_count).to eq(0)
26
+ end
27
+
28
+ it 'should expose a method for updating tracked attributes' do
29
+ expect(user).to respond_to(:update_tracked_fields!)
30
+ end
31
+
32
+ it { should validate_presence_of(:email) }
33
+ it { should validate_uniqueness_of(:email) }
34
+ it { should validate_uniqueness_of(:uid).scoped_to(:provider) }
35
+
36
+ it 'should support timeouts' do
37
+ expect(user.timeout_in).to be > 0
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'a secure Grape API' do
4
+ subject(:api_call) { get '/api/secure_resource' }
5
+
6
+ context 'with an authenticated user', :auth_request do
7
+ it 'should be successful' do
8
+ api_call
9
+ expect(response).to be_http_ok
10
+ end
11
+ end
12
+
13
+ context 'without an authenticated user' do
14
+ it 'should be unauthorized' do
15
+ api_call
16
+ expect(response).to be_http_unauthorized
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'a secure Rails API' do
4
+ describe 'POST request to an API-only action' do
5
+ subject(:api_call) { post '/rails_api/secure_resource' }
6
+
7
+ context 'with an authenticated user', :auth_request do
8
+ it 'should be successful' do
9
+ api_call
10
+ expect(response).to be_http_ok
11
+ end
12
+ end
13
+
14
+ context 'without an authenticated user' do
15
+ it 'should be unauthorized' do
16
+ api_call
17
+ expect(response).to be_http_unauthorized
18
+ end
19
+ end
20
+ end
21
+
22
+ describe 'GET json request to mixed API/website action' do
23
+ subject(:api_call) { get '/rails_api/secure_resource.json' }
24
+
25
+ context 'with an authenticated user', :auth_request do
26
+ it 'should be successful' do
27
+ api_call
28
+ expect(response).to be_http_ok
29
+ end
30
+ end
31
+
32
+ context 'without an authenticated user' do
33
+ it 'should be unauthorized' do
34
+ api_call
35
+ expect(response).to be_http_unauthorized
36
+ end
37
+ end
38
+ end
39
+
40
+ describe 'GET html request to mixed API/website action' do
41
+ subject(:website_call) { get '/rails_api/secure_resource.html' }
42
+
43
+ it 'should be a redirect' do
44
+ website_call
45
+ expect(response).to be_redirect
46
+ end
47
+
48
+ it 'should redirect to the new session path' do
49
+ website_call
50
+ expect(response).to redirect_to('/g5_auth/users/sign_in')
51
+ end
52
+ end
53
+ end