cbsorcery 0.8.6

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 (135) hide show
  1. data/.document +5 -0
  2. data/.gitignore +56 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +40 -0
  5. data/CHANGELOG.md +263 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +20 -0
  8. data/README.md +360 -0
  9. data/Rakefile +6 -0
  10. data/gemfiles/active_record-rails40.gemfile +7 -0
  11. data/gemfiles/active_record-rails41.gemfile +7 -0
  12. data/lib/generators/sorcery/USAGE +22 -0
  13. data/lib/generators/sorcery/helpers.rb +40 -0
  14. data/lib/generators/sorcery/install_generator.rb +95 -0
  15. data/lib/generators/sorcery/templates/initializer.rb +451 -0
  16. data/lib/generators/sorcery/templates/migration/activity_logging.rb +10 -0
  17. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +9 -0
  18. data/lib/generators/sorcery/templates/migration/core.rb +13 -0
  19. data/lib/generators/sorcery/templates/migration/external.rb +12 -0
  20. data/lib/generators/sorcery/templates/migration/remember_me.rb +8 -0
  21. data/lib/generators/sorcery/templates/migration/reset_password.rb +9 -0
  22. data/lib/generators/sorcery/templates/migration/user_activation.rb +9 -0
  23. data/lib/sorcery.rb +85 -0
  24. data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
  25. data/lib/sorcery/adapters/base_adapter.rb +30 -0
  26. data/lib/sorcery/controller.rb +157 -0
  27. data/lib/sorcery/controller/config.rb +65 -0
  28. data/lib/sorcery/controller/submodules/activity_logging.rb +82 -0
  29. data/lib/sorcery/controller/submodules/brute_force_protection.rb +38 -0
  30. data/lib/sorcery/controller/submodules/external.rb +199 -0
  31. data/lib/sorcery/controller/submodules/http_basic_auth.rb +74 -0
  32. data/lib/sorcery/controller/submodules/remember_me.rb +81 -0
  33. data/lib/sorcery/controller/submodules/session_timeout.rb +56 -0
  34. data/lib/sorcery/crypto_providers/aes256.rb +51 -0
  35. data/lib/sorcery/crypto_providers/bcrypt.rb +97 -0
  36. data/lib/sorcery/crypto_providers/common.rb +35 -0
  37. data/lib/sorcery/crypto_providers/md5.rb +19 -0
  38. data/lib/sorcery/crypto_providers/sha1.rb +28 -0
  39. data/lib/sorcery/crypto_providers/sha256.rb +36 -0
  40. data/lib/sorcery/crypto_providers/sha512.rb +36 -0
  41. data/lib/sorcery/engine.rb +21 -0
  42. data/lib/sorcery/model.rb +183 -0
  43. data/lib/sorcery/model/config.rb +96 -0
  44. data/lib/sorcery/model/submodules/activity_logging.rb +70 -0
  45. data/lib/sorcery/model/submodules/brute_force_protection.rb +125 -0
  46. data/lib/sorcery/model/submodules/external.rb +100 -0
  47. data/lib/sorcery/model/submodules/remember_me.rb +62 -0
  48. data/lib/sorcery/model/submodules/reset_password.rb +131 -0
  49. data/lib/sorcery/model/submodules/user_activation.rb +149 -0
  50. data/lib/sorcery/model/temporary_token.rb +30 -0
  51. data/lib/sorcery/protocols/certs/ca-bundle.crt +5182 -0
  52. data/lib/sorcery/protocols/oauth.rb +42 -0
  53. data/lib/sorcery/protocols/oauth2.rb +47 -0
  54. data/lib/sorcery/providers/base.rb +27 -0
  55. data/lib/sorcery/providers/facebook.rb +63 -0
  56. data/lib/sorcery/providers/github.rb +51 -0
  57. data/lib/sorcery/providers/google.rb +51 -0
  58. data/lib/sorcery/providers/jira.rb +77 -0
  59. data/lib/sorcery/providers/linkedin.rb +66 -0
  60. data/lib/sorcery/providers/liveid.rb +53 -0
  61. data/lib/sorcery/providers/twitter.rb +59 -0
  62. data/lib/sorcery/providers/vk.rb +63 -0
  63. data/lib/sorcery/providers/xing.rb +64 -0
  64. data/lib/sorcery/railties/tasks.rake +6 -0
  65. data/lib/sorcery/test_helpers/internal.rb +78 -0
  66. data/lib/sorcery/test_helpers/internal/rails.rb +68 -0
  67. data/lib/sorcery/test_helpers/rails/controller.rb +21 -0
  68. data/lib/sorcery/test_helpers/rails/integration.rb +26 -0
  69. data/lib/sorcery/version.rb +3 -0
  70. data/sorcery.gemspec +34 -0
  71. data/spec/active_record/user_activation_spec.rb +18 -0
  72. data/spec/active_record/user_activity_logging_spec.rb +17 -0
  73. data/spec/active_record/user_brute_force_protection_spec.rb +16 -0
  74. data/spec/active_record/user_oauth_spec.rb +16 -0
  75. data/spec/active_record/user_remember_me_spec.rb +16 -0
  76. data/spec/active_record/user_reset_password_spec.rb +16 -0
  77. data/spec/active_record/user_spec.rb +37 -0
  78. data/spec/controllers/controller_activity_logging_spec.rb +124 -0
  79. data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
  80. data/spec/controllers/controller_http_basic_auth_spec.rb +68 -0
  81. data/spec/controllers/controller_oauth2_spec.rb +407 -0
  82. data/spec/controllers/controller_oauth_spec.rb +240 -0
  83. data/spec/controllers/controller_remember_me_spec.rb +117 -0
  84. data/spec/controllers/controller_session_timeout_spec.rb +80 -0
  85. data/spec/controllers/controller_spec.rb +215 -0
  86. data/spec/orm/active_record.rb +21 -0
  87. data/spec/rails_app/app/active_record/authentication.rb +3 -0
  88. data/spec/rails_app/app/active_record/user.rb +5 -0
  89. data/spec/rails_app/app/active_record/user_provider.rb +3 -0
  90. data/spec/rails_app/app/controllers/sorcery_controller.rb +265 -0
  91. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  92. data/spec/rails_app/app/mailers/sorcery_mailer.rb +32 -0
  93. data/spec/rails_app/app/views/application/index.html.erb +17 -0
  94. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  95. data/spec/rails_app/app/views/sorcery_mailer/activation_email.html.erb +17 -0
  96. data/spec/rails_app/app/views/sorcery_mailer/activation_email.text.erb +9 -0
  97. data/spec/rails_app/app/views/sorcery_mailer/activation_needed_email.html.erb +17 -0
  98. data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.html.erb +17 -0
  99. data/spec/rails_app/app/views/sorcery_mailer/activation_success_email.text.erb +9 -0
  100. data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.html.erb +16 -0
  101. data/spec/rails_app/app/views/sorcery_mailer/reset_password_email.text.erb +8 -0
  102. data/spec/rails_app/app/views/sorcery_mailer/send_unlock_token_email.text.erb +1 -0
  103. data/spec/rails_app/config.ru +4 -0
  104. data/spec/rails_app/config/application.rb +56 -0
  105. data/spec/rails_app/config/boot.rb +4 -0
  106. data/spec/rails_app/config/database.yml +22 -0
  107. data/spec/rails_app/config/environment.rb +5 -0
  108. data/spec/rails_app/config/environments/test.rb +37 -0
  109. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  110. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  111. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  112. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  113. data/spec/rails_app/config/initializers/session_store.rb +12 -0
  114. data/spec/rails_app/config/locales/en.yml +5 -0
  115. data/spec/rails_app/config/routes.rb +48 -0
  116. data/spec/rails_app/db/migrate/activation/20101224223622_add_activation_to_users.rb +17 -0
  117. data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +19 -0
  118. data/spec/rails_app/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +13 -0
  119. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +16 -0
  120. data/spec/rails_app/db/migrate/external/20101224223628_create_authentications_and_user_providers.rb +22 -0
  121. data/spec/rails_app/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +15 -0
  122. data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +13 -0
  123. data/spec/rails_app/db/schema.rb +23 -0
  124. data/spec/rails_app/db/seeds.rb +7 -0
  125. data/spec/shared_examples/user_activation_shared_examples.rb +242 -0
  126. data/spec/shared_examples/user_activity_logging_shared_examples.rb +97 -0
  127. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +156 -0
  128. data/spec/shared_examples/user_oauth_shared_examples.rb +36 -0
  129. data/spec/shared_examples/user_remember_me_shared_examples.rb +57 -0
  130. data/spec/shared_examples/user_reset_password_shared_examples.rb +263 -0
  131. data/spec/shared_examples/user_shared_examples.rb +467 -0
  132. data/spec/sorcery_crypto_providers_spec.rb +198 -0
  133. data/spec/spec.opts +2 -0
  134. data/spec/spec_helper.rb +41 -0
  135. metadata +350 -0
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/user_activity_logging_shared_examples'
3
+
4
+ describe User, "with activity logging submodule", :active_record => true do
5
+
6
+ before(:all) do
7
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activity_logging")
8
+ User.reset_column_information
9
+ end
10
+
11
+ after(:all) do
12
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activity_logging")
13
+ end
14
+
15
+ it_behaves_like "rails_3_activity_logging_model"
16
+
17
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/user_brute_force_protection_shared_examples'
3
+
4
+ describe User, "with brute_force_protection submodule", :active_record => true do
5
+ before(:all) do
6
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/brute_force_protection")
7
+ User.reset_column_information
8
+ end
9
+
10
+ after(:all) do
11
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/brute_force_protection")
12
+ end
13
+
14
+ it_behaves_like "rails_3_brute_force_protection_model"
15
+
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/user_oauth_shared_examples'
3
+
4
+ describe User, "with oauth submodule", :active_record => true do
5
+ before(:all) do
6
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
7
+ User.reset_column_information
8
+ end
9
+
10
+ after(:all) do
11
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
12
+ end
13
+
14
+ it_behaves_like "rails_3_oauth_model"
15
+
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/user_remember_me_shared_examples'
3
+
4
+ describe User, "with remember_me submodule", :active_record => true do
5
+ before(:all) do
6
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/remember_me")
7
+ User.reset_column_information
8
+ end
9
+
10
+ after(:all) do
11
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/remember_me")
12
+ end
13
+
14
+ it_behaves_like "rails_3_remember_me_model"
15
+
16
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'shared_examples/user_reset_password_shared_examples'
3
+
4
+ describe User, "with reset_password submodule", :active_record => true do
5
+ before(:all) do
6
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/reset_password")
7
+ User.reset_column_information
8
+ end
9
+
10
+ after(:all) do
11
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/reset_password")
12
+ end
13
+
14
+ it_behaves_like "rails_3_reset_password_model"
15
+
16
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'rails_app/app/mailers/sorcery_mailer'
3
+ require 'shared_examples/user_shared_examples'
4
+
5
+ describe User, "with no submodules (core)", :active_record => true do
6
+ before(:all) do
7
+ sorcery_reload!
8
+ end
9
+
10
+ context "when app has plugin loaded" do
11
+ it "responds to the plugin activation class method" do
12
+ expect(ActiveRecord::Base).to respond_to :authenticates_with_sorcery!
13
+ end
14
+
15
+ it "User responds to .authenticates_with_sorcery!" do
16
+ expect(User).to respond_to :authenticates_with_sorcery!
17
+ end
18
+ end
19
+
20
+ # ----------------- PLUGIN CONFIGURATION -----------------------
21
+
22
+ it_should_behave_like "rails_3_core_model"
23
+
24
+ describe "external users" do
25
+ before(:all) do
26
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
27
+ User.reset_column_information
28
+ sorcery_reload!
29
+ end
30
+
31
+ after(:all) do
32
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
33
+ end
34
+
35
+ it_should_behave_like "external_user"
36
+ end
37
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ # require 'shared_examples/controller_activity_logging_shared_examples'
4
+
5
+ describe SorceryController do
6
+ after(:all) do
7
+ sorcery_controller_property_set(:register_login_time, true)
8
+ sorcery_controller_property_set(:register_logout_time, true)
9
+ sorcery_controller_property_set(:register_last_activity_time, true)
10
+ # sorcery_controller_property_set(:last_login_from_ip_address_name, true)
11
+ end
12
+
13
+ # ----------------- ACTIVITY LOGGING -----------------------
14
+ context "with activity logging features" do
15
+
16
+ let(:adapter) { double('sorcery_adapter') }
17
+ let(:user) { double('user', id: 42, sorcery_adapter: adapter) }
18
+
19
+ before(:all) do
20
+ sorcery_reload!([:activity_logging])
21
+ end
22
+
23
+ specify { expect(subject).to respond_to(:current_users) }
24
+
25
+ before(:each) do
26
+ allow(user).to receive(:username)
27
+ allow(user).to receive_message_chain(:sorcery_config, :username_attribute_names, :first) { :username }
28
+ allow(User.sorcery_config).to receive(:last_login_at_attribute_name) { :last_login_at }
29
+ allow(User.sorcery_config).to receive(:last_login_from_ip_address_name) { :last_login_from_ip_address }
30
+
31
+ sorcery_controller_property_set(:register_login_time, false)
32
+ sorcery_controller_property_set(:register_last_ip_address, false)
33
+ sorcery_controller_property_set(:register_last_activity_time, false)
34
+ end
35
+
36
+ it "'current_users' should proxy to User.current_users" do
37
+ expect(User).to receive(:current_users).with(no_args)
38
+
39
+ subject.current_users
40
+ end
41
+
42
+
43
+ it "logs login time on login" do
44
+ now = Time.now.in_time_zone
45
+ Timecop.freeze(now)
46
+
47
+ sorcery_controller_property_set(:register_login_time, true)
48
+ expect(user).to receive(:set_last_login_at).with(be_within(0.1).of(now))
49
+ login_user(user)
50
+
51
+ Timecop.return
52
+ end
53
+
54
+ it "logs logout time on logout" do
55
+ login_user(user)
56
+ now = Time.now.in_time_zone
57
+ Timecop.freeze(now)
58
+ expect(user).to receive(:set_last_logout_at).with(be_within(0.1).of(now))
59
+
60
+ logout_user
61
+
62
+ Timecop.return
63
+ end
64
+
65
+ it "logs last activity time when logged in" do
66
+ sorcery_controller_property_set(:register_last_activity_time, true)
67
+
68
+ login_user(user)
69
+ now = Time.now.in_time_zone
70
+ Timecop.freeze(now)
71
+ expect(user).to receive(:set_last_activity_at).with(be_within(0.1).of(now))
72
+
73
+ get :some_action
74
+
75
+ Timecop.return
76
+ end
77
+
78
+ it "logs last IP address when logged in" do
79
+ sorcery_controller_property_set(:register_last_ip_address, true)
80
+ expect(user).to receive(:set_last_ip_addess).with('0.0.0.0')
81
+
82
+ login_user(user)
83
+ end
84
+
85
+ it "updates nothing but activity fields" do
86
+ pending 'Move to model'
87
+ original_user_name = User.last.username
88
+ login_user(user)
89
+ get :some_action_making_a_non_persisted_change_to_the_user
90
+
91
+ expect(User.last.username).to eq original_user_name
92
+ end
93
+
94
+ it "does not register login time if configured so" do
95
+ sorcery_controller_property_set(:register_login_time, false)
96
+
97
+ expect(user).to receive(:set_last_login_at).never
98
+ login_user(user)
99
+ end
100
+
101
+ it "does not register logout time if configured so" do
102
+ sorcery_controller_property_set(:register_logout_time, false)
103
+ login_user(user)
104
+
105
+ expect(user).to receive(:set_last_logout_at).never
106
+ logout_user
107
+ end
108
+
109
+ it "does not register last activity time if configured so" do
110
+ sorcery_controller_property_set(:register_last_activity_time, false)
111
+
112
+ expect(user).to receive(:set_last_activity_at).never
113
+ login_user(user)
114
+ end
115
+
116
+ it "does not register last IP address if configured so" do
117
+ sorcery_controller_property_set(:register_last_ip_address, false)
118
+ expect(user).to receive(:set_last_ip_addess).never
119
+
120
+ login_user(user)
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe SorceryController do
4
+
5
+ let(:user) { double('user', id: 42, email: 'bla@bla.com') }
6
+
7
+ def request_test_login
8
+ get :test_login, email: 'bla@bla.com', password: 'blabla'
9
+ end
10
+
11
+ # ----------------- SESSION TIMEOUT -----------------------
12
+ describe "brute force protection features" do
13
+
14
+ before(:all) do
15
+ sorcery_reload!([:brute_force_protection])
16
+ end
17
+
18
+ after(:each) do
19
+ Sorcery::Controller::Config.reset!
20
+ sorcery_controller_property_set(:user_class, User)
21
+ Timecop.return
22
+ end
23
+
24
+ it "counts login retries" do
25
+ allow(User).to receive(:authenticate)
26
+ allow(User.sorcery_adapter).to receive(:find_by_credentials).with(['bla@bla.com', 'blabla']).and_return(user)
27
+
28
+ expect(user).to receive(:register_failed_login!).exactly(3).times
29
+
30
+ 3.times { request_test_login }
31
+ end
32
+
33
+ it "resets the counter on a good login" do
34
+ # dirty hack for rails 4
35
+ allow(@controller).to receive(:register_last_activity_time_to_db)
36
+
37
+ allow(User).to receive(:authenticate).and_return(user)
38
+ expect(user).to receive_message_chain(:sorcery_adapter, :update_attribute).with(:failed_logins_count, 0)
39
+
40
+ get :test_login, email: 'bla@bla.com', password: 'secret'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ describe SorceryController do
4
+
5
+ let(:user) { double("user", id: 42, email: 'bla@bla.com') }
6
+
7
+ describe "with http basic auth features" do
8
+ before(:all) do
9
+ sorcery_reload!([:http_basic_auth])
10
+
11
+ sorcery_controller_property_set(:controller_to_realm_map, {"sorcery" => "sorcery"})
12
+ end
13
+
14
+ after(:each) do
15
+ logout_user
16
+ end
17
+
18
+ it "requests basic authentication when before_filter is used" do
19
+ get :test_http_basic_auth
20
+
21
+ expect(response.status).to eq 401
22
+ end
23
+
24
+ it "authenticates from http basic if credentials are sent" do
25
+ # dirty hack for rails 4
26
+ allow(subject).to receive(:register_last_activity_time_to_db)
27
+
28
+ @request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64::encode64("#{user.email}:secret")}"
29
+ expect(User).to receive('authenticate').with('bla@bla.com', 'secret').and_return(user)
30
+ get :test_http_basic_auth, nil, http_authentication_used: true
31
+
32
+ expect(response).to be_a_success
33
+ end
34
+
35
+ it "fails authentication if credentials are wrong" do
36
+ @request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64::encode64("#{user.email}:wrong!")}"
37
+ expect(User).to receive('authenticate').with('bla@bla.com', 'wrong!').and_return(nil)
38
+ get :test_http_basic_auth, nil, http_authentication_used: true
39
+
40
+ expect(response).to redirect_to root_url
41
+ end
42
+
43
+ it "allows configuration option 'controller_to_realm_map'" do
44
+ sorcery_controller_property_set(:controller_to_realm_map, {"1" => "2"})
45
+
46
+ expect(Sorcery::Controller::Config.controller_to_realm_map).to eq({"1" => "2"})
47
+ end
48
+
49
+ it "displays the correct realm name configured for the controller" do
50
+ sorcery_controller_property_set(:controller_to_realm_map, {"sorcery" => "Salad"})
51
+ get :test_http_basic_auth
52
+
53
+ expect(response.headers["WWW-Authenticate"]).to eq "Basic realm=\"Salad\""
54
+ end
55
+
56
+ it "signs in the user's session on successful login" do
57
+ # dirty hack for rails 4
58
+ allow(controller).to receive(:register_last_activity_time_to_db)
59
+
60
+ @request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64::encode64("#{user.email}:secret")}"
61
+ expect(User).to receive('authenticate').with('bla@bla.com', 'secret').and_return(user)
62
+
63
+ get :test_http_basic_auth, nil, http_authentication_used: true
64
+
65
+ expect(session[:user_id]).to eq "42"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,407 @@
1
+ require 'spec_helper'
2
+
3
+ # require 'shared_examples/controller_oauth2_shared_examples'
4
+
5
+ describe SorceryController, :active_record => true do
6
+ before(:all) do
7
+ if SORCERY_ORM == :active_record
8
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
9
+ User.reset_column_information
10
+ end
11
+
12
+ sorcery_reload!([:external])
13
+ set_external_property
14
+ end
15
+
16
+ after(:all) do
17
+ if SORCERY_ORM == :active_record
18
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
19
+ end
20
+ end
21
+
22
+ describe 'using create_from' do
23
+ before(:each) do
24
+ stub_all_oauth2_requests!
25
+ end
26
+
27
+ it 'creates a new user' do
28
+ sorcery_model_property_set(:authentications_class, Authentication)
29
+ sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'name' })
30
+
31
+ expect(User).to receive(:create_from_provider).with('facebook', '123', {username: 'Noam Ben Ari'})
32
+ get :test_create_from_provider, provider: 'facebook'
33
+ end
34
+
35
+ it 'supports nested attributes' do
36
+ sorcery_model_property_set(:authentications_class, Authentication)
37
+ sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'hometown/name' })
38
+ expect(User).to receive(:create_from_provider).with('facebook', '123', {username: 'Haifa, Israel'})
39
+
40
+ get :test_create_from_provider, provider: 'facebook'
41
+ end
42
+
43
+ it 'does not crash on missing nested attributes' do
44
+ sorcery_model_property_set(:authentications_class, Authentication)
45
+ sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'name', created_at: 'does/not/exist' })
46
+
47
+ expect(User).to receive(:create_from_provider).with('facebook', '123', {username: 'Noam Ben Ari'})
48
+
49
+ get :test_create_from_provider, provider: 'facebook'
50
+ end
51
+
52
+ describe 'with a block' do
53
+ it 'does not create user' do
54
+ sorcery_model_property_set(:authentications_class, Authentication)
55
+ sorcery_controller_external_property_set(:facebook, :user_info_mapping, { username: 'name' })
56
+
57
+ u = double('user')
58
+ expect(User).to receive(:create_from_provider).with('facebook', '123', {username: 'Noam Ben Ari'}).and_return(u).and_yield(u)
59
+ # test_create_from_provider_with_block in controller will check for uniqueness of username
60
+ get :test_create_from_provider_with_block, provider: 'facebook'
61
+ end
62
+ end
63
+ end
64
+
65
+ # ----------------- OAuth -----------------------
66
+ context "with OAuth features" do
67
+
68
+ let(:user) { double('user', id: 42) }
69
+
70
+ before(:each) do
71
+ stub_all_oauth2_requests!
72
+ end
73
+
74
+ after(:each) do
75
+ User.sorcery_adapter.delete_all
76
+ Authentication.sorcery_adapter.delete_all
77
+ end
78
+
79
+ context "when callback_url begin with /" do
80
+ before do
81
+ sorcery_controller_external_property_set(:facebook, :callback_url, "/oauth/twitter/callback")
82
+ end
83
+ it "login_at redirects correctly" do
84
+ get :login_at_test_facebook
85
+ expect(response).to be_a_redirect
86
+ expect(response).to redirect_to("https://graph.facebook.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.facebook.key}&display=page&redirect_uri=http%3A%2F%2Ftest.host%2Foauth%2Ftwitter%2Fcallback&response_type=code&scope=email%2Coffline_access&state=")
87
+ end
88
+ it "logins with state" do
89
+ get :login_at_test_with_state
90
+ expect(response).to be_a_redirect
91
+ expect(response).to redirect_to("https://graph.facebook.com/oauth/authorize?client_id=#{::Sorcery::Controller::Config.facebook.key}&display=page&redirect_uri=http%3A%2F%2Ftest.host%2Foauth%2Ftwitter%2Fcallback&response_type=code&scope=email%2Coffline_access&state=bla")
92
+ end
93
+ after do
94
+ sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com")
95
+ end
96
+ end
97
+
98
+ #this test can never pass because of the previous test (the callback url can't change anymore)
99
+ =begin
100
+ context "when callback_url begin with http://" do
101
+ it "login_at redirects correctly" do
102
+ create_new_user
103
+ get :login_at_test2
104
+ response.should be_a_redirect
105
+ response.should redirect_to("https://graph.facebook.com/oauth/authorize?response_type=code&client_id=#{::Sorcery::Controller::Config.facebook.key}&redirect_uri=http%3A%2F%2Fblabla.com&scope=email%2Coffline_access&display=page&state")
106
+ end
107
+ end
108
+ =end
109
+ it "'login_from' logins if user exists" do
110
+ # dirty hack for rails 4
111
+ allow(subject).to receive(:register_last_activity_time_to_db)
112
+
113
+ sorcery_model_property_set(:authentications_class, Authentication)
114
+ expect(User).to receive(:load_from_provider).with(:facebook, '123').and_return(user)
115
+ get :test_login_from_facebook
116
+
117
+ expect(flash[:notice]).to eq "Success!"
118
+ end
119
+
120
+ it "'login_from' fails if user doesn't exist" do
121
+ sorcery_model_property_set(:authentications_class, Authentication)
122
+ expect(User).to receive(:load_from_provider).with(:facebook, '123').and_return(nil)
123
+ get :test_login_from_facebook
124
+
125
+ expect(flash[:alert]).to eq "Failed!"
126
+ end
127
+
128
+ it "on successful login_from the user is redirected to the url he originally wanted" do
129
+ # dirty hack for rails 4
130
+ allow(subject).to receive(:register_last_activity_time_to_db)
131
+
132
+ sorcery_model_property_set(:authentications_class, Authentication)
133
+ expect(User).to receive(:load_from_provider).with(:facebook, '123').and_return(user)
134
+ get :test_return_to_with_external_facebook, {}, :return_to_url => "fuu"
135
+
136
+ expect(response).to redirect_to("fuu")
137
+ expect(flash[:notice]).to eq "Success!"
138
+ end
139
+
140
+ [:github, :google, :liveid, :vk].each do |provider|
141
+
142
+ describe "with #{provider}" do
143
+
144
+ it "login_at redirects correctly" do
145
+ get :"login_at_test_#{provider}"
146
+
147
+ expect(response).to be_a_redirect
148
+ expect(response).to redirect_to(provider_url provider)
149
+ end
150
+
151
+ it "'login_from' logins if user exists" do
152
+ # dirty hack for rails 4
153
+ allow(subject).to receive(:register_last_activity_time_to_db)
154
+
155
+ sorcery_model_property_set(:authentications_class, Authentication)
156
+ expect(User).to receive(:load_from_provider).with(provider, '123').and_return(user)
157
+ get :"test_login_from_#{provider}"
158
+
159
+ expect(flash[:notice]).to eq "Success!"
160
+ end
161
+
162
+ it "'login_from' fails if user doesn't exist" do
163
+ sorcery_model_property_set(:authentications_class, Authentication)
164
+ expect(User).to receive(:load_from_provider).with(provider, '123').and_return(nil)
165
+ get :"test_login_from_#{provider}"
166
+
167
+ expect(flash[:alert]).to eq "Failed!"
168
+ end
169
+
170
+ it "on successful login_from the user is redirected to the url he originally wanted (#{provider})" do
171
+ # dirty hack for rails 4
172
+ allow(subject).to receive(:register_last_activity_time_to_db)
173
+
174
+ sorcery_model_property_set(:authentications_class, Authentication)
175
+ expect(User).to receive(:load_from_provider).with(provider, '123').and_return(user)
176
+ get :"test_return_to_with_external_#{provider}", {}, :return_to_url => "fuu"
177
+
178
+ expect(response).to redirect_to "fuu"
179
+ expect(flash[:notice]).to eq "Success!"
180
+ end
181
+ end
182
+ end
183
+
184
+ end
185
+
186
+ describe "OAuth with User Activation features" do
187
+ before(:all) do
188
+ if SORCERY_ORM == :active_record
189
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation")
190
+ end
191
+
192
+ sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer)
193
+ sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk])
194
+
195
+ sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w")
196
+ sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
197
+ sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com")
198
+ sorcery_controller_external_property_set(:github, :key, "eYVNBjBDi33aa9GkA3w")
199
+ sorcery_controller_external_property_set(:github, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
200
+ sorcery_controller_external_property_set(:github, :callback_url, "http://blabla.com")
201
+ sorcery_controller_external_property_set(:google, :key, "eYVNBjBDi33aa9GkA3w")
202
+ sorcery_controller_external_property_set(:google, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
203
+ sorcery_controller_external_property_set(:google, :callback_url, "http://blabla.com")
204
+ sorcery_controller_external_property_set(:liveid, :key, "eYVNBjBDi33aa9GkA3w")
205
+ sorcery_controller_external_property_set(:liveid, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
206
+ sorcery_controller_external_property_set(:liveid, :callback_url, "http://blabla.com")
207
+ sorcery_controller_external_property_set(:vk, :key, "eYVNBjBDi33aa9GkA3w")
208
+ sorcery_controller_external_property_set(:vk, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
209
+ sorcery_controller_external_property_set(:vk, :callback_url, "http://blabla.com")
210
+ end
211
+
212
+ after(:all) do
213
+ if SORCERY_ORM == :active_record
214
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation")
215
+ end
216
+ end
217
+
218
+ after(:each) do
219
+ User.sorcery_adapter.delete_all
220
+ end
221
+
222
+ it "does not send activation email to external users" do
223
+ old_size = ActionMailer::Base.deliveries.size
224
+ create_new_external_user(:facebook)
225
+
226
+ expect(ActionMailer::Base.deliveries.size).to eq old_size
227
+ end
228
+
229
+ it "does not send external users an activation success email" do
230
+ sorcery_model_property_set(:activation_success_email_method_name, nil)
231
+ create_new_external_user(:facebook)
232
+ old_size = ActionMailer::Base.deliveries.size
233
+ @user.activate!
234
+
235
+ expect(ActionMailer::Base.deliveries.size).to eq old_size
236
+ end
237
+
238
+ [:github, :google, :liveid, :vk].each do |provider|
239
+ it "does not send activation email to external users (#{provider})" do
240
+ old_size = ActionMailer::Base.deliveries.size
241
+ create_new_external_user provider
242
+ expect(ActionMailer::Base.deliveries.size).to eq old_size
243
+ end
244
+
245
+ it "does not send external users an activation success email (#{provider})" do
246
+ sorcery_model_property_set(:activation_success_email_method_name, nil)
247
+ create_new_external_user provider
248
+ old_size = ActionMailer::Base.deliveries.size
249
+ @user.activate!
250
+ end
251
+ end
252
+ end
253
+
254
+ describe "OAuth with user activation features" do
255
+
256
+ let(:user) { double('user', id: 42) }
257
+
258
+ before(:all) do
259
+ sorcery_reload!([:activity_logging, :external])
260
+ end
261
+
262
+ after(:all) do
263
+ if SORCERY_ORM == :active_record
264
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
265
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activity_logging")
266
+ end
267
+ end
268
+
269
+ %w(facebook github google liveid vk).each do |provider|
270
+ context "when #{provider}" do
271
+ before(:each) do
272
+ sorcery_controller_property_set(:register_login_time, true)
273
+ sorcery_controller_property_set(:register_logout_time, false)
274
+ sorcery_controller_property_set(:register_last_activity_time, false)
275
+ sorcery_controller_property_set(:register_last_ip_address, false)
276
+ stub_all_oauth2_requests!
277
+ sorcery_model_property_set(:authentications_class, Authentication)
278
+ end
279
+
280
+ it "registers login time" do
281
+ now = Time.now.in_time_zone
282
+ Timecop.freeze(now)
283
+ expect(User).to receive(:load_from_provider).and_return(user)
284
+ expect(user).to receive(:set_last_login_at).with(be_within(0.1).of(now))
285
+ get "test_login_from_#{provider}".to_sym
286
+ Timecop.return
287
+ end
288
+
289
+ it "does not register login time if configured so" do
290
+ sorcery_controller_property_set(:register_login_time, false)
291
+ now = Time.now.in_time_zone
292
+ Timecop.freeze(now)
293
+ expect(User).to receive(:load_from_provider).and_return(user)
294
+ expect(user).to receive(:set_last_login_at).never
295
+ get "test_login_from_#{provider}".to_sym
296
+
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ describe "OAuth with session timeout features" do
303
+ before(:all) do
304
+ sorcery_reload!([:session_timeout, :external])
305
+ end
306
+
307
+ let(:user) { double('user', id: 42) }
308
+
309
+ %w(facebook github google liveid vk).each do |provider|
310
+ context "when #{provider}" do
311
+ before(:each) do
312
+ sorcery_model_property_set(:authentications_class, Authentication)
313
+ sorcery_controller_property_set(:session_timeout,0.5)
314
+ stub_all_oauth2_requests!
315
+ end
316
+
317
+ after(:each) do
318
+ Timecop.return
319
+ end
320
+
321
+ it "does not reset session before session timeout" do
322
+ expect(User).to receive(:load_from_provider).with(provider.to_sym, '123').and_return(user)
323
+ get "test_login_from_#{provider}".to_sym
324
+
325
+ expect(session[:user_id]).not_to be_nil
326
+ expect(flash[:notice]).to eq "Success!"
327
+ end
328
+
329
+ it "resets session after session timeout" do
330
+ expect(User).to receive(:load_from_provider).with(provider.to_sym, '123').and_return(user)
331
+ get "test_login_from_#{provider}".to_sym
332
+ expect(session[:user_id]).to eq "42"
333
+ Timecop.travel(Time.now.in_time_zone+0.6)
334
+ get :test_should_be_logged_in
335
+
336
+ expect(session[:user_id]).to be_nil
337
+ expect(response).to be_a_redirect
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+ def stub_all_oauth2_requests!
344
+ access_token = double(OAuth2::AccessToken)
345
+ allow(access_token).to receive(:token_param=)
346
+ response = double(OAuth2::Response)
347
+ allow(response).to receive(:body) { {
348
+ "id"=>"123",
349
+ "name"=>"Noam Ben Ari",
350
+ "first_name"=>"Noam",
351
+ "last_name"=>"Ben Ari",
352
+ "link"=>"http://www.facebook.com/nbenari1",
353
+ "hometown"=>{"id"=>"110619208966868", "name"=>"Haifa, Israel"},
354
+ "location"=>{"id"=>"106906559341067", "name"=>"Pardes Hanah, Hefa, Israel"},
355
+ "bio"=>"I'm a new daddy, and enjoying it!",
356
+ "gender"=>"male",
357
+ "email"=>"nbenari@gmail.com",
358
+ "timezone"=>2,
359
+ "locale"=>"en_US",
360
+ "languages"=>[{"id"=>"108405449189952", "name"=>"Hebrew"}, {"id"=>"106059522759137", "name"=>"English"}, {"id"=>"112624162082677", "name"=>"Russian"}],
361
+ "verified"=>true,
362
+ "updated_time"=>"2011-02-16T20:59:38+0000",
363
+ # response for VK auth
364
+ "response"=>[
365
+ {
366
+ "uid"=>"123",
367
+ "first_name"=>"Noam",
368
+ "last_name"=>"Ben Ari"
369
+ }
370
+ ]}.to_json }
371
+ allow(access_token).to receive(:get) { response }
372
+ allow(access_token).to receive(:token) { "187041a618229fdaf16613e96e1caabc1e86e46bbfad228de41520e63fe45873684c365a14417289599f3" }
373
+ # access_token params for VK auth
374
+ allow(access_token).to receive(:params) { { "user_id"=>"100500", "email"=>"nbenari@gmail.com" } }
375
+ allow_any_instance_of(OAuth2::Strategy::AuthCode).to receive(:get_token) { access_token }
376
+ end
377
+
378
+ def set_external_property
379
+ sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk])
380
+ sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w")
381
+ sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
382
+ sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com")
383
+ sorcery_controller_external_property_set(:github, :key, "eYVNBjBDi33aa9GkA3w")
384
+ sorcery_controller_external_property_set(:github, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
385
+ sorcery_controller_external_property_set(:github, :callback_url, "http://blabla.com")
386
+ sorcery_controller_external_property_set(:google, :key, "eYVNBjBDi33aa9GkA3w")
387
+ sorcery_controller_external_property_set(:google, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
388
+ sorcery_controller_external_property_set(:google, :callback_url, "http://blabla.com")
389
+ sorcery_controller_external_property_set(:liveid, :key, "eYVNBjBDi33aa9GkA3w")
390
+ sorcery_controller_external_property_set(:liveid, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
391
+ sorcery_controller_external_property_set(:liveid, :callback_url, "http://blabla.com")
392
+ sorcery_controller_external_property_set(:vk, :key, "eYVNBjBDi33aa9GkA3w")
393
+ sorcery_controller_external_property_set(:vk, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
394
+ sorcery_controller_external_property_set(:vk, :callback_url, "http://blabla.com")
395
+ end
396
+
397
+ def provider_url(provider)
398
+ {
399
+ github: "https://github.com/login/oauth/authorize?client_id=#{::Sorcery::Controller::Config.github.key}&display=&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=&state=",
400
+ google: "https://accounts.google.com/o/oauth2/auth?client_id=#{::Sorcery::Controller::Config.google.key}&display=&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&state=",
401
+ liveid: "https://oauth.live.com/authorize?client_id=#{::Sorcery::Controller::Config.liveid.key}&display=&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=wl.basic+wl.emails+wl.offline_access&state=",
402
+ vk: "https://oauth.vk.com/authorize?client_id=#{::Sorcery::Controller::Config.vk.key}&display=&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.vk.scope}&state="
403
+ }[provider]
404
+ end
405
+
406
+ end
407
+