sorcery 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sorcery might be problematic. Click here for more details.

Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +75 -14
  4. data/CHANGELOG.md +23 -1
  5. data/Gemfile +1 -0
  6. data/README.md +137 -86
  7. data/gemfiles/active_record-rails40.gemfile +7 -0
  8. data/gemfiles/active_record-rails41.gemfile +3 -2
  9. data/gemfiles/mongo_mapper-rails40.gemfile +9 -0
  10. data/gemfiles/mongo_mapper-rails41.gemfile +2 -1
  11. data/gemfiles/mongoid-rails40.gemfile +9 -0
  12. data/gemfiles/mongoid-rails41.gemfile +3 -5
  13. data/gemfiles/mongoid3-rails32.gemfile +9 -0
  14. data/lib/generators/sorcery/USAGE +1 -1
  15. data/lib/generators/sorcery/install_generator.rb +19 -5
  16. data/lib/generators/sorcery/templates/initializer.rb +34 -9
  17. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +3 -1
  18. data/lib/generators/sorcery/templates/migration/core.rb +2 -2
  19. data/lib/generators/sorcery/templates/migration/external.rb +3 -1
  20. data/lib/sorcery.rb +75 -43
  21. data/lib/sorcery/adapters/active_record_adapter.rb +120 -0
  22. data/lib/sorcery/adapters/base_adapter.rb +30 -0
  23. data/lib/sorcery/adapters/data_mapper_adapter.rb +176 -0
  24. data/lib/sorcery/adapters/mongo_mapper_adapter.rb +110 -0
  25. data/lib/sorcery/adapters/mongoid_adapter.rb +97 -0
  26. data/lib/sorcery/controller.rb +5 -64
  27. data/lib/sorcery/controller/config.rb +65 -0
  28. data/lib/sorcery/controller/submodules/activity_logging.rb +16 -21
  29. data/lib/sorcery/controller/submodules/brute_force_protection.rb +6 -6
  30. data/lib/sorcery/controller/submodules/external.rb +8 -28
  31. data/lib/sorcery/controller/submodules/remember_me.rb +4 -4
  32. data/lib/sorcery/controller/submodules/session_timeout.rb +10 -6
  33. data/lib/sorcery/model.rb +43 -175
  34. data/lib/sorcery/model/config.rb +96 -0
  35. data/lib/sorcery/model/submodules/activity_logging.rb +29 -36
  36. data/lib/sorcery/model/submodules/brute_force_protection.rb +21 -37
  37. data/lib/sorcery/model/submodules/external.rb +53 -9
  38. data/lib/sorcery/model/submodules/remember_me.rb +12 -31
  39. data/lib/sorcery/model/submodules/reset_password.rb +21 -39
  40. data/lib/sorcery/model/submodules/user_activation.rb +21 -63
  41. data/lib/sorcery/model/temporary_token.rb +4 -4
  42. data/lib/sorcery/providers/base.rb +11 -0
  43. data/lib/sorcery/providers/facebook.rb +1 -1
  44. data/lib/sorcery/providers/github.rb +1 -1
  45. data/lib/sorcery/providers/google.rb +1 -1
  46. data/lib/sorcery/providers/heroku.rb +57 -0
  47. data/lib/sorcery/providers/jira.rb +77 -0
  48. data/lib/sorcery/providers/linkedin.rb +1 -1
  49. data/lib/sorcery/providers/liveid.rb +1 -1
  50. data/lib/sorcery/providers/salesforce.rb +50 -0
  51. data/lib/sorcery/providers/twitter.rb +1 -1
  52. data/lib/sorcery/providers/vk.rb +6 -4
  53. data/lib/sorcery/providers/xing.rb +1 -1
  54. data/lib/sorcery/test_helpers/internal.rb +7 -3
  55. data/lib/sorcery/test_helpers/rails/controller.rb +5 -1
  56. data/lib/sorcery/version.rb +3 -0
  57. data/sorcery.gemspec +6 -2
  58. data/spec/active_record/user_activity_logging_spec.rb +9 -0
  59. data/spec/controllers/controller_activity_logging_spec.rb +124 -0
  60. data/spec/controllers/controller_brute_force_protection_spec.rb +43 -0
  61. data/spec/{active_record → controllers}/controller_http_basic_auth_spec.rb +14 -11
  62. data/spec/{active_record → controllers}/controller_oauth2_spec.rb +128 -56
  63. data/spec/{active_record → controllers}/controller_oauth_spec.rb +94 -70
  64. data/spec/{active_record → controllers}/controller_remember_me_spec.rb +32 -12
  65. data/spec/{active_record → controllers}/controller_session_timeout_spec.rb +15 -5
  66. data/spec/{shared_examples/controller_shared_examples.rb → controllers/controller_spec.rb} +34 -19
  67. data/spec/{datamapper → data_mapper}/user_activation_spec.rb +1 -1
  68. data/spec/data_mapper/user_activity_logging_spec.rb +14 -0
  69. data/spec/{datamapper → data_mapper}/user_brute_force_protection_spec.rb +1 -1
  70. data/spec/{datamapper → data_mapper}/user_oauth_spec.rb +1 -1
  71. data/spec/{datamapper → data_mapper}/user_remember_me_spec.rb +1 -1
  72. data/spec/{datamapper → data_mapper}/user_reset_password_spec.rb +1 -1
  73. data/spec/{datamapper → data_mapper}/user_spec.rb +1 -1
  74. data/spec/mongoid/user_spec.rb +13 -0
  75. data/spec/orm/active_record.rb +12 -0
  76. data/spec/orm/{datamapper.rb → data_mapper.rb} +16 -2
  77. data/spec/orm/mongo_mapper.rb +0 -1
  78. data/spec/orm/mongoid.rb +4 -0
  79. data/spec/rails_app/app/controllers/sorcery_controller.rb +62 -1
  80. data/spec/rails_app/app/{datamapper → data_mapper}/authentication.rb +0 -0
  81. data/spec/rails_app/app/{datamapper → data_mapper}/user.rb +0 -0
  82. data/spec/rails_app/app/mongo_mapper/user.rb +2 -0
  83. data/spec/rails_app/config/routes.rb +9 -0
  84. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +2 -2
  85. data/spec/shared_examples/user_activation_shared_examples.rb +7 -7
  86. data/spec/shared_examples/user_activity_logging_shared_examples.rb +73 -5
  87. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +127 -9
  88. data/spec/shared_examples/user_oauth_shared_examples.rb +3 -6
  89. data/spec/shared_examples/user_remember_me_shared_examples.rb +6 -3
  90. data/spec/shared_examples/user_reset_password_shared_examples.rb +10 -10
  91. data/spec/shared_examples/user_shared_examples.rb +117 -30
  92. data/spec/spec_helper.rb +7 -22
  93. metadata +36 -58
  94. data/Gemfile.rails4 +0 -22
  95. data/VERSION +0 -1
  96. data/lib/sorcery/model/adapters/active_record.rb +0 -54
  97. data/lib/sorcery/model/adapters/datamapper.rb +0 -123
  98. data/lib/sorcery/model/adapters/mongo_mapper.rb +0 -60
  99. data/lib/sorcery/model/adapters/mongoid.rb +0 -88
  100. data/lib/sorcery/test_helpers/rails.rb +0 -7
  101. data/spec/active_record/controller_activity_logging_spec.rb +0 -29
  102. data/spec/active_record/controller_brute_force_protection_spec.rb +0 -158
  103. data/spec/active_record/controller_spec.rb +0 -8
  104. data/spec/active_record/integration_spec.rb +0 -23
  105. data/spec/datamapper/controller_activity_logging_spec.rb +0 -17
  106. data/spec/datamapper/controller_spec.rb +0 -8
  107. data/spec/datamapper/user_activity_logging_spec.rb +0 -9
  108. data/spec/mongo_mapper/controller_spec.rb +0 -8
  109. data/spec/mongoid/controller_activity_logging_spec.rb +0 -16
  110. data/spec/mongoid/controller_spec.rb +0 -8
  111. data/spec/rails_app/public/404.html +0 -26
  112. data/spec/rails_app/public/422.html +0 -26
  113. data/spec/rails_app/public/500.html +0 -26
  114. data/spec/rails_app/public/favicon.ico +0 -0
  115. data/spec/rails_app/public/images/rails.png +0 -0
  116. data/spec/rails_app/public/javascripts/application.js +0 -2
  117. data/spec/rails_app/public/javascripts/controls.js +0 -965
  118. data/spec/rails_app/public/javascripts/dragdrop.js +0 -974
  119. data/spec/rails_app/public/javascripts/effects.js +0 -1123
  120. data/spec/rails_app/public/javascripts/prototype.js +0 -6001
  121. data/spec/rails_app/public/javascripts/rails.js +0 -175
  122. data/spec/rails_app/public/robots.txt +0 -5
  123. data/spec/rails_app/public/stylesheets/.gitkeep +0 -0
  124. data/spec/shared_examples/controller_activity_logging_shared_examples.rb +0 -125
  125. data/spec/shared_examples/controller_oauth2_shared_examples.rb +0 -52
  126. data/spec/shared_examples/controller_oauth_shared_examples.rb +0 -62
@@ -26,7 +26,7 @@ module Sorcery
26
26
  access_token.token_param = 'access_token'
27
27
  response = access_token.get(user_info_url)
28
28
 
29
- {}.tap do |h|
29
+ auth_hash(access_token).tap do |h|
30
30
  h[:user_info] = JSON.parse(response.body)
31
31
  h[:uid] = h[:user_info]['id']
32
32
  end
@@ -0,0 +1,50 @@
1
+ module Sorcery
2
+ module Providers
3
+ # This class adds support for OAuth with salesforce.com.
4
+ #
5
+ # config.salesforce.key = <key>
6
+ # config.salesforce.secret = <secret>
7
+ # ...
8
+ #
9
+ class Salesforce < Base
10
+
11
+ include Protocols::Oauth2
12
+
13
+ attr_accessor :auth_url, :token_url, :scope
14
+
15
+ def initialize
16
+ super
17
+
18
+ @site = 'https://login.salesforce.com'
19
+ @auth_url = '/services/oauth2/authorize'
20
+ @token_url = '/services/oauth2/token'
21
+ end
22
+
23
+ def get_user_hash(access_token)
24
+ user_info_url = access_token.params['id']
25
+ response = access_token.get(user_info_url)
26
+
27
+ auth_hash(access_token).tap do |h|
28
+ h[:user_info] = JSON.parse(response.body)
29
+ h[:uid] = h[:user_info]['user_id']
30
+ end
31
+ end
32
+
33
+ # calculates and returns the url to which the user should be redirected,
34
+ # to get authenticated at the external provider's site.
35
+ def login_url(params, session)
36
+ authorize_url({ authorize_url: auth_url })
37
+ end
38
+
39
+ # tries to login the user from access token
40
+ def process_callback(params, session)
41
+ args = {}.tap do |a|
42
+ a[:code] = params[:code] if params[:code]
43
+ end
44
+
45
+ get_access_token(args, token_url: token_url, token_method: :post)
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -27,7 +27,7 @@ module Sorcery
27
27
  def get_user_hash(access_token)
28
28
  response = access_token.get(user_info_path)
29
29
 
30
- {}.tap do |h|
30
+ auth_hash(access_token).tap do |h|
31
31
  h[:user_info] = JSON.parse(response.body)
32
32
  h[:uid] = h[:user_info]['id'].to_s
33
33
  end
@@ -19,24 +19,26 @@ module Sorcery
19
19
  @user_info_url = 'https://api.vk.com/method/getProfiles'
20
20
  @auth_path = '/authorize'
21
21
  @token_path = '/access_token'
22
- @scope = ''
22
+ @scope = 'email'
23
23
  end
24
24
 
25
25
  def get_user_hash(access_token)
26
- user_hash = {}
26
+ user_hash = auth_hash(access_token)
27
27
 
28
28
  params = {
29
29
  access_token: access_token.token,
30
30
  uids: access_token.params['user_id'],
31
31
  fields: user_info_mapping.values.join(','),
32
- scope: scope,
32
+ scope: scope
33
33
  }
34
34
 
35
35
  response = access_token.get(user_info_url, params: params)
36
36
  if user_hash[:user_info] = JSON.parse(response.body)
37
37
  user_hash[:user_info] = user_hash[:user_info]['response'][0]
38
- user_hash[:user_info]['full_name'] = [user_hash[:user_info]['first_name'], user_hash[:user_info]['last_name']].join
38
+ user_hash[:user_info]['full_name'] = [user_hash[:user_info]['first_name'], user_hash[:user_info]['last_name']].join(' ')
39
+
39
40
  user_hash[:uid] = user_hash[:user_info]['uid']
41
+ user_hash[:user_info]['email'] = access_token.params['email']
40
42
  end
41
43
  user_hash
42
44
  end
@@ -32,7 +32,7 @@ module Sorcery
32
32
  def get_user_hash(access_token)
33
33
  response = access_token.get(user_info_path)
34
34
 
35
- {}.tap do |h|
35
+ auth_hash(access_token).tap do |h|
36
36
  h[:user_info] = JSON.parse(response.body)['users'].first
37
37
  h[:uid] = user_hash[:user_info]['id'].to_s
38
38
  end
@@ -30,14 +30,14 @@ module Sorcery
30
30
 
31
31
  def create_new_user(attributes_hash = nil)
32
32
  @user = build_new_user(attributes_hash)
33
- @user.sorcery_save(:raise_on_failure => true)
33
+ @user.sorcery_adapter.save(:raise_on_failure => true)
34
34
  @user
35
35
  end
36
36
 
37
37
  def create_new_external_user(provider, attributes_hash = nil)
38
38
  user_attributes_hash = attributes_hash || {:username => 'gizmo'}
39
39
  @user = User.new(user_attributes_hash)
40
- @user.sorcery_save(:raise_on_failure => true)
40
+ @user.sorcery_adapter.save(:raise_on_failure => true)
41
41
  @user.authentications.create!({:provider => provider, :uid => 123})
42
42
  @user
43
43
  end
@@ -47,7 +47,7 @@ module Sorcery
47
47
 
48
48
  user_attributes_hash = attributes_hash || {:username => 'gizmo'}
49
49
  @user = User.new(user_attributes_hash)
50
- @user.sorcery_save(:raise_on_failure => true)
50
+ @user.sorcery_adapter.save(:raise_on_failure => true)
51
51
  @user.send(authentication_association).create!({:provider => provider, :uid => 123})
52
52
  @user
53
53
  end
@@ -58,6 +58,10 @@ module Sorcery
58
58
  end
59
59
  end
60
60
 
61
+ def update_model(&block)
62
+ User.class_exec(&block)
63
+ end
64
+
61
65
  private
62
66
 
63
67
  # reload user class between specs
@@ -1,7 +1,7 @@
1
1
  module Sorcery
2
2
  module TestHelpers
3
3
  module Rails
4
- module Controller
4
+ module Controller
5
5
  def login_user(user = nil, test_context = {})
6
6
  user ||= @user
7
7
  @controller.send(:auto_login, user)
@@ -11,6 +11,10 @@ module Sorcery
11
11
  def logout_user
12
12
  @controller.send(:logout)
13
13
  end
14
+
15
+ def logged_in?
16
+ @controller.send(:logged_in?)
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -0,0 +1,3 @@
1
+ module Sorcery
2
+ VERSION = "0.9.0"
3
+ end
@@ -1,6 +1,10 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'sorcery/version'
4
+
1
5
  Gem::Specification.new do |s|
2
6
  s.name = "sorcery"
3
- s.version = "0.8.6"
7
+ s.version = Sorcery::VERSION
4
8
  s.authors = ["Noam Ben Ari", "Kir Shatrov", "Grzegorz Witek"]
5
9
  s.email = "nbenari@gmail.com"
6
10
  s.description = "Provides common authentication needs such as signing in/out, activating by email and resetting password."
@@ -15,7 +19,7 @@ Gem::Specification.new do |s|
15
19
  s.required_ruby_version = '>= 1.9.3'
16
20
 
17
21
  s.add_dependency "oauth", "~> 0.4", ">= 0.4.4"
18
- s.add_dependency "oauth2", ">= 0.8.0", "< 1.0.0"
22
+ s.add_dependency "oauth2", ">= 0.8.0"
19
23
  s.add_dependency "bcrypt", "~> 3.1"
20
24
 
21
25
  s.add_development_dependency "abstract", ">= 1.0.0"
@@ -3,6 +3,15 @@ require 'shared_examples/user_activity_logging_shared_examples'
3
3
 
4
4
  describe User, "with activity logging submodule", :active_record => true do
5
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
+
6
15
  it_behaves_like "rails_3_activity_logging_model"
7
16
 
8
17
  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
@@ -1,10 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe SorceryController, :active_record => true do
3
+ describe SorceryController do
4
4
 
5
- let!(:user) { create_new_user }
5
+ let(:user) { double("user", id: 42, email: 'bla@bla.com') }
6
6
 
7
- # ----------------- HTTP BASIC AUTH -----------------------
8
7
  describe "with http basic auth features" do
9
8
  before(:all) do
10
9
  sorcery_reload!([:http_basic_auth])
@@ -19,22 +18,24 @@ describe SorceryController, :active_record => true do
19
18
  it "requests basic authentication when before_filter is used" do
20
19
  get :test_http_basic_auth
21
20
 
22
- expect(response.code).to eq "401"
21
+ expect(response.status).to eq 401
23
22
  end
24
23
 
25
24
  it "authenticates from http basic if credentials are sent" do
26
25
  # dirty hack for rails 4
27
26
  allow(subject).to receive(:register_last_activity_time_to_db)
28
27
 
29
- @request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{user.email}:secret")
30
- get :test_http_basic_auth, nil, :http_authentication_used => true
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
31
 
32
32
  expect(response).to be_a_success
33
33
  end
34
34
 
35
35
  it "fails authentication if credentials are wrong" do
36
- @request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{user.email}:wrong!")
37
- get :test_http_basic_auth, nil, :http_authentication_used => true
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
38
39
 
39
40
  expect(response).to redirect_to root_url
40
41
  end
@@ -56,10 +57,12 @@ describe SorceryController, :active_record => true do
56
57
  # dirty hack for rails 4
57
58
  allow(controller).to receive(:register_last_activity_time_to_db)
58
59
 
59
- @request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{user.email}:secret")
60
- get :test_http_basic_auth, nil, :http_authentication_used => true
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)
61
62
 
62
- expect(session[:user_id]).to be User.find_by_email(user.email).id
63
+ get :test_http_basic_auth, nil, http_authentication_used: true
64
+
65
+ expect(session[:user_id]).to eq "42"
63
66
  end
64
67
  end
65
68
  end
@@ -1,32 +1,79 @@
1
1
  require 'spec_helper'
2
2
 
3
- require 'shared_examples/controller_oauth2_shared_examples'
3
+ # require 'shared_examples/controller_oauth2_shared_examples'
4
4
 
5
5
  describe SorceryController, :active_record => true do
6
6
  before(:all) do
7
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
8
- User.reset_column_information
7
+ if SORCERY_ORM == :active_record
8
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
9
+ User.reset_column_information
10
+ end
9
11
 
10
12
  sorcery_reload!([:external])
11
13
  set_external_property
12
14
  end
13
15
 
14
16
  after(:all) do
15
- ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
17
+ if SORCERY_ORM == :active_record
18
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
19
+ end
16
20
  end
17
21
 
18
- it_behaves_like "oauth2_controller"
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
19
64
 
20
65
  # ----------------- OAuth -----------------------
21
66
  context "with OAuth features" do
22
67
 
68
+ let(:user) { double('user', id: 42) }
69
+
23
70
  before(:each) do
24
71
  stub_all_oauth2_requests!
25
72
  end
26
73
 
27
74
  after(:each) do
28
- User.delete_all
29
- Authentication.delete_all
75
+ User.sorcery_adapter.delete_all
76
+ Authentication.sorcery_adapter.delete_all
30
77
  end
31
78
 
32
79
  context "when callback_url begin with /" do
@@ -34,13 +81,11 @@ describe SorceryController, :active_record => true do
34
81
  sorcery_controller_external_property_set(:facebook, :callback_url, "/oauth/twitter/callback")
35
82
  end
36
83
  it "login_at redirects correctly" do
37
- create_new_user
38
84
  get :login_at_test_facebook
39
85
  expect(response).to be_a_redirect
40
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=")
41
87
  end
42
88
  it "logins with state" do
43
- create_new_user
44
89
  get :login_at_test_with_state
45
90
  expect(response).to be_a_redirect
46
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")
@@ -66,7 +111,7 @@ describe SorceryController, :active_record => true do
66
111
  allow(subject).to receive(:register_last_activity_time_to_db)
67
112
 
68
113
  sorcery_model_property_set(:authentications_class, Authentication)
69
- create_new_external_user(:facebook)
114
+ expect(User).to receive(:load_from_provider).with(:facebook, '123').and_return(user)
70
115
  get :test_login_from_facebook
71
116
 
72
117
  expect(flash[:notice]).to eq "Success!"
@@ -74,7 +119,7 @@ describe SorceryController, :active_record => true do
74
119
 
75
120
  it "'login_from' fails if user doesn't exist" do
76
121
  sorcery_model_property_set(:authentications_class, Authentication)
77
- create_new_user
122
+ expect(User).to receive(:load_from_provider).with(:facebook, '123').and_return(nil)
78
123
  get :test_login_from_facebook
79
124
 
80
125
  expect(flash[:alert]).to eq "Failed!"
@@ -85,19 +130,18 @@ describe SorceryController, :active_record => true do
85
130
  allow(subject).to receive(:register_last_activity_time_to_db)
86
131
 
87
132
  sorcery_model_property_set(:authentications_class, Authentication)
88
- create_new_external_user(:facebook)
133
+ expect(User).to receive(:load_from_provider).with(:facebook, '123').and_return(user)
89
134
  get :test_return_to_with_external_facebook, {}, :return_to_url => "fuu"
90
135
 
91
136
  expect(response).to redirect_to("fuu")
92
137
  expect(flash[:notice]).to eq "Success!"
93
138
  end
94
139
 
95
- [:github, :google, :liveid].each do |provider|
140
+ [:github, :google, :liveid, :vk, :salesforce].each do |provider|
96
141
 
97
142
  describe "with #{provider}" do
98
143
 
99
144
  it "login_at redirects correctly" do
100
- create_new_user
101
145
  get :"login_at_test_#{provider}"
102
146
 
103
147
  expect(response).to be_a_redirect
@@ -109,7 +153,7 @@ describe SorceryController, :active_record => true do
109
153
  allow(subject).to receive(:register_last_activity_time_to_db)
110
154
 
111
155
  sorcery_model_property_set(:authentications_class, Authentication)
112
- create_new_external_user(provider)
156
+ expect(User).to receive(:load_from_provider).with(provider, '123').and_return(user)
113
157
  get :"test_login_from_#{provider}"
114
158
 
115
159
  expect(flash[:notice]).to eq "Success!"
@@ -117,18 +161,18 @@ describe SorceryController, :active_record => true do
117
161
 
118
162
  it "'login_from' fails if user doesn't exist" do
119
163
  sorcery_model_property_set(:authentications_class, Authentication)
120
- create_new_user
164
+ expect(User).to receive(:load_from_provider).with(provider, '123').and_return(nil)
121
165
  get :"test_login_from_#{provider}"
122
166
 
123
167
  expect(flash[:alert]).to eq "Failed!"
124
168
  end
125
169
 
126
- it "on successful login_from the user is redirected to the url he originally wanted (github)" do
170
+ it "on successful login_from the user is redirected to the url he originally wanted (#{provider})" do
127
171
  # dirty hack for rails 4
128
172
  allow(subject).to receive(:register_last_activity_time_to_db)
129
173
 
130
174
  sorcery_model_property_set(:authentications_class, Authentication)
131
- create_new_external_user(provider)
175
+ expect(User).to receive(:load_from_provider).with(provider, '123').and_return(user)
132
176
  get :"test_return_to_with_external_#{provider}", {}, :return_to_url => "fuu"
133
177
 
134
178
  expect(response).to redirect_to "fuu"
@@ -141,9 +185,13 @@ describe SorceryController, :active_record => true do
141
185
 
142
186
  describe "OAuth with User Activation features" do
143
187
  before(:all) do
144
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation")
188
+ if SORCERY_ORM == :active_record
189
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation")
190
+ end
191
+
145
192
  sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer)
146
- sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid])
193
+ sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce])
194
+
147
195
  sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w")
148
196
  sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
149
197
  sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com")
@@ -156,15 +204,22 @@ describe SorceryController, :active_record => true do
156
204
  sorcery_controller_external_property_set(:liveid, :key, "eYVNBjBDi33aa9GkA3w")
157
205
  sorcery_controller_external_property_set(:liveid, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
158
206
  sorcery_controller_external_property_set(:liveid, :callback_url, "http://blabla.com")
159
-
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
+ sorcery_controller_external_property_set(:salesforce, :key, "eYVNBjBDi33aa9GkA3w")
211
+ sorcery_controller_external_property_set(:salesforce, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
212
+ sorcery_controller_external_property_set(:salesforce, :callback_url, "http://blabla.com")
160
213
  end
161
214
 
162
215
  after(:all) do
163
- ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation")
216
+ if SORCERY_ORM == :active_record
217
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation")
218
+ end
164
219
  end
165
220
 
166
221
  after(:each) do
167
- User.delete_all
222
+ User.sorcery_adapter.delete_all
168
223
  end
169
224
 
170
225
  it "does not send activation email to external users" do
@@ -183,64 +238,65 @@ describe SorceryController, :active_record => true do
183
238
  expect(ActionMailer::Base.deliveries.size).to eq old_size
184
239
  end
185
240
 
186
- # provider: github
187
- [:github, :google, :liveid].each do |provider|
188
- it "does not send activation email to external users (github)" do
241
+ [:github, :google, :liveid, :vk, :salesforce].each do |provider|
242
+ it "does not send activation email to external users (#{provider})" do
189
243
  old_size = ActionMailer::Base.deliveries.size
190
244
  create_new_external_user provider
191
245
  expect(ActionMailer::Base.deliveries.size).to eq old_size
192
246
  end
193
247
 
194
- it "does not send external users an activation success email (github)" do
248
+ it "does not send external users an activation success email (#{provider})" do
195
249
  sorcery_model_property_set(:activation_success_email_method_name, nil)
196
250
  create_new_external_user provider
197
251
  old_size = ActionMailer::Base.deliveries.size
198
252
  @user.activate!
199
-
200
- expect(ActionMailer::Base.deliveries.size).to eq old_size
201
253
  end
202
254
  end
203
255
  end
204
256
 
205
257
  describe "OAuth with user activation features" do
258
+
259
+ let(:user) { double('user', id: 42) }
260
+
206
261
  before(:all) do
207
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
208
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activity_logging")
209
- User.reset_column_information
210
262
  sorcery_reload!([:activity_logging, :external])
211
263
  end
212
264
 
213
265
  after(:all) do
214
- ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
215
- ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activity_logging")
266
+ if SORCERY_ORM == :active_record
267
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
268
+ ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activity_logging")
269
+ end
216
270
  end
217
271
 
218
- %w(facebook github google liveid).each do |provider|
272
+ %w(facebook github google liveid vk salesforce).each do |provider|
219
273
  context "when #{provider}" do
220
274
  before(:each) do
221
- User.delete_all
222
- Authentication.delete_all
223
275
  sorcery_controller_property_set(:register_login_time, true)
276
+ sorcery_controller_property_set(:register_logout_time, false)
277
+ sorcery_controller_property_set(:register_last_activity_time, false)
278
+ sorcery_controller_property_set(:register_last_ip_address, false)
224
279
  stub_all_oauth2_requests!
225
280
  sorcery_model_property_set(:authentications_class, Authentication)
226
- create_new_external_user(provider.to_sym)
227
281
  end
228
282
 
229
283
  it "registers login time" do
230
284
  now = Time.now.in_time_zone
285
+ Timecop.freeze(now)
286
+ expect(User).to receive(:load_from_provider).and_return(user)
287
+ expect(user).to receive(:set_last_login_at).with(be_within(0.1).of(now))
231
288
  get "test_login_from_#{provider}".to_sym
232
-
233
- expect(User.last.last_login_at).not_to be_nil
234
- expect(User.last.last_login_at.to_s(:db)).to be >= now.to_s(:db)
235
- expect(User.last.last_login_at.to_s(:db)).to be <= (now+2).to_s(:db)
289
+ Timecop.return
236
290
  end
237
291
 
238
292
  it "does not register login time if configured so" do
239
293
  sorcery_controller_property_set(:register_login_time, false)
240
294
  now = Time.now.in_time_zone
295
+ Timecop.freeze(now)
296
+ expect(User).to receive(:load_from_provider).and_return(user)
297
+ expect(user).to receive(:set_last_login_at).never
241
298
  get "test_login_from_#{provider}".to_sym
242
299
 
243
- expect(User.last.last_login_at).to be_nil
244
300
  end
245
301
  end
246
302
  end
@@ -248,24 +304,17 @@ describe SorceryController, :active_record => true do
248
304
 
249
305
  describe "OAuth with session timeout features" do
250
306
  before(:all) do
251
- ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
252
- User.reset_column_information
253
307
  sorcery_reload!([:session_timeout, :external])
254
308
  end
255
309
 
256
- after(:all) do
257
- ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
258
- end
310
+ let(:user) { double('user', id: 42) }
259
311
 
260
- %w(facebook github google liveid).each do |provider|
312
+ %w(facebook github google liveid vk salesforce).each do |provider|
261
313
  context "when #{provider}" do
262
314
  before(:each) do
263
- User.delete_all
264
- Authentication.delete_all
265
315
  sorcery_model_property_set(:authentications_class, Authentication)
266
316
  sorcery_controller_property_set(:session_timeout,0.5)
267
317
  stub_all_oauth2_requests!
268
- create_new_external_user(provider.to_sym)
269
318
  end
270
319
 
271
320
  after(:each) do
@@ -273,6 +322,7 @@ describe SorceryController, :active_record => true do
273
322
  end
274
323
 
275
324
  it "does not reset session before session timeout" do
325
+ expect(User).to receive(:load_from_provider).with(provider.to_sym, '123').and_return(user)
276
326
  get "test_login_from_#{provider}".to_sym
277
327
 
278
328
  expect(session[:user_id]).not_to be_nil
@@ -280,7 +330,9 @@ describe SorceryController, :active_record => true do
280
330
  end
281
331
 
282
332
  it "resets session after session timeout" do
333
+ expect(User).to receive(:load_from_provider).with(provider.to_sym, '123').and_return(user)
283
334
  get "test_login_from_#{provider}".to_sym
335
+ expect(session[:user_id]).to eq "42"
284
336
  Timecop.travel(Time.now.in_time_zone+0.6)
285
337
  get :test_should_be_logged_in
286
338
 
@@ -297,6 +349,7 @@ describe SorceryController, :active_record => true do
297
349
  response = double(OAuth2::Response)
298
350
  allow(response).to receive(:body) { {
299
351
  "id"=>"123",
352
+ "user_id"=>"123", # Needed for Salesforce
300
353
  "name"=>"Noam Ben Ari",
301
354
  "first_name"=>"Noam",
302
355
  "last_name"=>"Ben Ari",
@@ -310,13 +363,24 @@ describe SorceryController, :active_record => true do
310
363
  "locale"=>"en_US",
311
364
  "languages"=>[{"id"=>"108405449189952", "name"=>"Hebrew"}, {"id"=>"106059522759137", "name"=>"English"}, {"id"=>"112624162082677", "name"=>"Russian"}],
312
365
  "verified"=>true,
313
- "updated_time"=>"2011-02-16T20:59:38+0000"}.to_json }
366
+ "updated_time"=>"2011-02-16T20:59:38+0000",
367
+ # response for VK auth
368
+ "response"=>[
369
+ {
370
+ "uid"=>"123",
371
+ "first_name"=>"Noam",
372
+ "last_name"=>"Ben Ari"
373
+ }
374
+ ]}.to_json }
314
375
  allow(access_token).to receive(:get) { response }
376
+ allow(access_token).to receive(:token) { "187041a618229fdaf16613e96e1caabc1e86e46bbfad228de41520e63fe45873684c365a14417289599f3" }
377
+ # access_token params for VK auth
378
+ allow(access_token).to receive(:params) { { "user_id"=>"100500", "email"=>"nbenari@gmail.com" } }
315
379
  allow_any_instance_of(OAuth2::Strategy::AuthCode).to receive(:get_token) { access_token }
316
380
  end
317
381
 
318
382
  def set_external_property
319
- sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid])
383
+ sorcery_controller_property_set(:external_providers, [:facebook, :github, :google, :liveid, :vk, :salesforce])
320
384
  sorcery_controller_external_property_set(:facebook, :key, "eYVNBjBDi33aa9GkA3w")
321
385
  sorcery_controller_external_property_set(:facebook, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
322
386
  sorcery_controller_external_property_set(:facebook, :callback_url, "http://blabla.com")
@@ -329,14 +393,22 @@ describe SorceryController, :active_record => true do
329
393
  sorcery_controller_external_property_set(:liveid, :key, "eYVNBjBDi33aa9GkA3w")
330
394
  sorcery_controller_external_property_set(:liveid, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
331
395
  sorcery_controller_external_property_set(:liveid, :callback_url, "http://blabla.com")
396
+ sorcery_controller_external_property_set(:vk, :key, "eYVNBjBDi33aa9GkA3w")
397
+ sorcery_controller_external_property_set(:vk, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
398
+ sorcery_controller_external_property_set(:vk, :callback_url, "http://blabla.com")
399
+ sorcery_controller_external_property_set(:salesforce, :key, "eYVNBjBDi33aa9GkA3w")
400
+ sorcery_controller_external_property_set(:salesforce, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
401
+ sorcery_controller_external_property_set(:salesforce, :callback_url, "http://blabla.com")
332
402
  end
333
403
 
334
404
  def provider_url(provider)
335
405
  {
336
406
  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=",
337
407
  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=",
338
- 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="
408
+ 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=",
409
+ 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=",
410
+ salesforce: "https://login.salesforce.com/services/oauth2/authorize?client_id=#{::Sorcery::Controller::Config.salesforce.key}&display=&redirect_uri=http%3A%2F%2Fblabla.com&response_type=code&scope=#{::Sorcery::Controller::Config.salesforce.scope}&state="
339
411
  }[provider]
340
412
  end
341
-
342
413
  end
414
+