sorcery 0.11.0 → 0.15.1

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 (106) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +20 -0
  3. data/.rubocop.yml +55 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +3 -52
  6. data/CHANGELOG.md +69 -0
  7. data/Gemfile +3 -3
  8. data/{LICENSE.txt → LICENSE.md} +1 -1
  9. data/README.md +34 -7
  10. data/lib/generators/sorcery/USAGE +1 -1
  11. data/lib/generators/sorcery/install_generator.rb +21 -21
  12. data/lib/generators/sorcery/templates/initializer.rb +164 -69
  13. data/lib/generators/sorcery/templates/migration/activity_logging.rb +4 -4
  14. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +3 -3
  15. data/lib/generators/sorcery/templates/migration/core.rb +2 -2
  16. data/lib/generators/sorcery/templates/migration/external.rb +3 -3
  17. data/lib/generators/sorcery/templates/migration/magic_login.rb +9 -0
  18. data/lib/generators/sorcery/templates/migration/remember_me.rb +2 -2
  19. data/lib/generators/sorcery/templates/migration/reset_password.rb +4 -3
  20. data/lib/generators/sorcery/templates/migration/user_activation.rb +3 -3
  21. data/lib/sorcery.rb +2 -0
  22. data/lib/sorcery/adapters/active_record_adapter.rb +3 -2
  23. data/lib/sorcery/adapters/mongoid_adapter.rb +23 -11
  24. data/lib/sorcery/controller.rb +26 -15
  25. data/lib/sorcery/controller/config.rb +2 -0
  26. data/lib/sorcery/controller/submodules/activity_logging.rb +14 -3
  27. data/lib/sorcery/controller/submodules/brute_force_protection.rb +7 -3
  28. data/lib/sorcery/controller/submodules/external.rb +48 -33
  29. data/lib/sorcery/controller/submodules/http_basic_auth.rb +5 -1
  30. data/lib/sorcery/controller/submodules/remember_me.rb +9 -10
  31. data/lib/sorcery/controller/submodules/session_timeout.rb +32 -6
  32. data/lib/sorcery/crypto_providers/aes256.rb +2 -1
  33. data/lib/sorcery/crypto_providers/bcrypt.rb +8 -2
  34. data/lib/sorcery/engine.rb +16 -3
  35. data/lib/sorcery/model.rb +14 -10
  36. data/lib/sorcery/model/config.rb +12 -4
  37. data/lib/sorcery/model/submodules/brute_force_protection.rb +6 -7
  38. data/lib/sorcery/model/submodules/external.rb +19 -3
  39. data/lib/sorcery/model/submodules/magic_login.rb +130 -0
  40. data/lib/sorcery/model/submodules/reset_password.rb +25 -2
  41. data/lib/sorcery/model/submodules/user_activation.rb +1 -1
  42. data/lib/sorcery/model/temporary_token.rb +3 -1
  43. data/lib/sorcery/protocols/oauth.rb +1 -0
  44. data/lib/sorcery/providers/auth0.rb +46 -0
  45. data/lib/sorcery/providers/discord.rb +52 -0
  46. data/lib/sorcery/providers/heroku.rb +1 -0
  47. data/lib/sorcery/providers/instagram.rb +73 -0
  48. data/lib/sorcery/providers/line.rb +47 -0
  49. data/lib/sorcery/providers/linkedin.rb +45 -36
  50. data/lib/sorcery/providers/vk.rb +5 -4
  51. data/lib/sorcery/providers/wechat.rb +8 -6
  52. data/lib/sorcery/test_helpers/internal.rb +5 -4
  53. data/lib/sorcery/test_helpers/internal/rails.rb +11 -11
  54. data/lib/sorcery/test_helpers/rails/request.rb +20 -0
  55. data/lib/sorcery/version.rb +1 -1
  56. data/sorcery.gemspec +28 -11
  57. data/spec/active_record/user_activation_spec.rb +2 -2
  58. data/spec/active_record/user_activity_logging_spec.rb +2 -2
  59. data/spec/active_record/user_brute_force_protection_spec.rb +2 -2
  60. data/spec/active_record/user_magic_login_spec.rb +15 -0
  61. data/spec/active_record/user_oauth_spec.rb +2 -2
  62. data/spec/active_record/user_remember_me_spec.rb +2 -2
  63. data/spec/active_record/user_reset_password_spec.rb +2 -2
  64. data/spec/active_record/user_spec.rb +0 -10
  65. data/spec/controllers/controller_http_basic_auth_spec.rb +1 -1
  66. data/spec/controllers/controller_oauth2_spec.rb +212 -123
  67. data/spec/controllers/controller_oauth_spec.rb +7 -7
  68. data/spec/controllers/controller_remember_me_spec.rb +16 -8
  69. data/spec/controllers/controller_session_timeout_spec.rb +90 -3
  70. data/spec/controllers/controller_spec.rb +13 -3
  71. data/spec/orm/active_record.rb +2 -2
  72. data/spec/providers/example_provider_spec.rb +17 -0
  73. data/spec/providers/example_spec.rb +17 -0
  74. data/spec/providers/vk_spec.rb +42 -0
  75. data/spec/rails_app/app/assets/config/manifest.js +1 -0
  76. data/spec/rails_app/app/controllers/sorcery_controller.rb +131 -32
  77. data/spec/rails_app/app/mailers/sorcery_mailer.rb +7 -0
  78. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb +13 -0
  79. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb +6 -0
  80. data/spec/rails_app/config/application.rb +8 -3
  81. data/spec/rails_app/config/boot.rb +1 -1
  82. data/spec/rails_app/config/environment.rb +1 -1
  83. data/spec/rails_app/config/routes.rb +14 -0
  84. data/spec/rails_app/config/secrets.yml +4 -0
  85. data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +2 -2
  86. data/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb +9 -0
  87. data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +17 -0
  88. data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +2 -0
  89. data/spec/rails_app/db/schema.rb +7 -9
  90. data/spec/shared_examples/user_magic_login_shared_examples.rb +150 -0
  91. data/spec/shared_examples/user_oauth_shared_examples.rb +1 -1
  92. data/spec/shared_examples/user_remember_me_shared_examples.rb +1 -1
  93. data/spec/shared_examples/user_reset_password_shared_examples.rb +37 -5
  94. data/spec/shared_examples/user_shared_examples.rb +104 -43
  95. data/spec/sorcery_crypto_providers_spec.rb +61 -1
  96. data/spec/sorcery_temporary_token_spec.rb +27 -0
  97. data/spec/spec.opts +1 -1
  98. data/spec/spec_helper.rb +2 -2
  99. data/spec/support/migration_helper.rb +19 -0
  100. data/spec/support/providers/example.rb +11 -0
  101. data/spec/support/providers/example_provider.rb +11 -0
  102. metadata +89 -33
  103. data/gemfiles/active_record-rails40.gemfile +0 -7
  104. data/gemfiles/active_record-rails41.gemfile +0 -7
  105. data/gemfiles/active_record-rails42.gemfile +0 -7
  106. data/spec/rails_app/config/initializers/secret_token.rb +0 -7
@@ -1,9 +1,9 @@
1
1
  class SorceryActivityLogging < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :last_login_at, :datetime, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :last_logout_at, :datetime, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :last_activity_at, :datetime, :default => nil
6
- add_column :<%= model_class_name.tableize %>, :last_login_from_ip_address, :string, :default => nil
3
+ add_column :<%= model_class_name.tableize %>, :last_login_at, :datetime, default: nil
4
+ add_column :<%= model_class_name.tableize %>, :last_logout_at, :datetime, default: nil
5
+ add_column :<%= model_class_name.tableize %>, :last_activity_at, :datetime, default: nil
6
+ add_column :<%= model_class_name.tableize %>, :last_login_from_ip_address, :string, default: nil
7
7
 
8
8
  add_index :<%= model_class_name.tableize %>, [:last_logout_at, :last_activity_at]
9
9
  end
@@ -1,8 +1,8 @@
1
1
  class SorceryBruteForceProtection < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :failed_logins_count, :integer, :default => 0
4
- add_column :<%= model_class_name.tableize %>, :lock_expires_at, :datetime, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :unlock_token, :string, :default => nil
3
+ add_column :<%= model_class_name.tableize %>, :failed_logins_count, :integer, default: 0
4
+ add_column :<%= model_class_name.tableize %>, :lock_expires_at, :datetime, default: nil
5
+ add_column :<%= model_class_name.tableize %>, :unlock_token, :string, default: nil
6
6
 
7
7
  add_index :<%= model_class_name.tableize %>, :unlock_token
8
8
  end
@@ -1,11 +1,11 @@
1
1
  class SorceryCore < <%= migration_class_name %>
2
2
  def change
3
3
  create_table :<%= model_class_name.tableize %> do |t|
4
- t.string :email, :null => false
4
+ t.string :email, null: false
5
5
  t.string :crypted_password
6
6
  t.string :salt
7
7
 
8
- t.timestamps :null => false
8
+ t.timestamps null: false
9
9
  end
10
10
 
11
11
  add_index :<%= model_class_name.tableize %>, :email, unique: true
@@ -1,10 +1,10 @@
1
1
  class SorceryExternal < <%= migration_class_name %>
2
2
  def change
3
3
  create_table :authentications do |t|
4
- t.integer :<%= model_class_name.tableize.singularize %>_id, :null => false
5
- t.string :provider, :uid, :null => false
4
+ t.integer :<%= model_class_name.tableize.singularize %>_id, null: false
5
+ t.string :provider, :uid, null: false
6
6
 
7
- t.timestamps :null => false
7
+ t.timestamps null: false
8
8
  end
9
9
 
10
10
  add_index :authentications, [:provider, :uid]
@@ -0,0 +1,9 @@
1
+ class SorceryMagicLogin < <%= migration_class_name %>
2
+ def change
3
+ add_column :<%= model_class_name.tableize %>, :magic_login_token, :string, default: nil
4
+ add_column :<%= model_class_name.tableize %>, :magic_login_token_expires_at, :datetime, default: nil
5
+ add_column :<%= model_class_name.tableize %>, :magic_login_email_sent_at, :datetime, default: nil
6
+
7
+ add_index :<%= model_class_name.tableize %>, :magic_login_token
8
+ end
9
+ end
@@ -1,7 +1,7 @@
1
1
  class SorceryRememberMe < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :remember_me_token, :string, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :remember_me_token_expires_at, :datetime, :default => nil
3
+ add_column :<%= model_class_name.tableize %>, :remember_me_token, :string, default: nil
4
+ add_column :<%= model_class_name.tableize %>, :remember_me_token_expires_at, :datetime, default: nil
5
5
 
6
6
  add_index :<%= model_class_name.tableize %>, :remember_me_token
7
7
  end
@@ -1,8 +1,9 @@
1
1
  class SorceryResetPassword < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, :default => nil
3
+ add_column :<%= model_class_name.tableize %>, :reset_password_token, :string, default: nil
4
+ add_column :<%= model_class_name.tableize %>, :reset_password_token_expires_at, :datetime, default: nil
5
+ add_column :<%= model_class_name.tableize %>, :reset_password_email_sent_at, :datetime, default: nil
6
+ add_column :<%= model_class_name.tableize %>, :access_count_to_reset_password_page, :integer, default: 0
6
7
 
7
8
  add_index :<%= model_class_name.tableize %>, :reset_password_token
8
9
  end
@@ -1,8 +1,8 @@
1
1
  class SorceryUserActivation < <%= migration_class_name %>
2
2
  def change
3
- add_column :<%= model_class_name.tableize %>, :activation_state, :string, :default => nil
4
- add_column :<%= model_class_name.tableize %>, :activation_token, :string, :default => nil
5
- add_column :<%= model_class_name.tableize %>, :activation_token_expires_at, :datetime, :default => nil
3
+ add_column :<%= model_class_name.tableize %>, :activation_state, :string, default: nil
4
+ add_column :<%= model_class_name.tableize %>, :activation_token, :string, default: nil
5
+ add_column :<%= model_class_name.tableize %>, :activation_token_expires_at, :datetime, default: nil
6
6
 
7
7
  add_index :<%= model_class_name.tableize %>, :activation_token
8
8
  end
data/lib/sorcery.rb CHANGED
@@ -18,6 +18,7 @@ module Sorcery
18
18
  require 'sorcery/model/submodules/activity_logging'
19
19
  require 'sorcery/model/submodules/brute_force_protection'
20
20
  require 'sorcery/model/submodules/external'
21
+ require 'sorcery/model/submodules/magic_login'
21
22
  end
22
23
  end
23
24
 
@@ -56,6 +57,7 @@ module Sorcery
56
57
  module Rails
57
58
  require 'sorcery/test_helpers/rails/controller'
58
59
  require 'sorcery/test_helpers/rails/integration'
60
+ require 'sorcery/test_helpers/rails/request'
59
61
  end
60
62
 
61
63
  module Internal
@@ -6,7 +6,8 @@ module Sorcery
6
6
  @model.send(:"#{name}=", value)
7
7
  end
8
8
  primary_key = @model.class.primary_key
9
- @model.class.where(:"#{primary_key}" => @model.send(:"#{primary_key}")).update_all(attrs)
9
+ updated_count = @model.class.where(:"#{primary_key}" => @model.send(:"#{primary_key}")).update_all(attrs)
10
+ updated_count == 1
10
11
  end
11
12
 
12
13
  def save(options = {})
@@ -34,7 +35,7 @@ module Sorcery
34
35
  end
35
36
 
36
37
  def define_callback(time, event, method_name, options = {})
37
- @klass.send "#{time}_#{event}", method_name, options.slice(:if)
38
+ @klass.send "#{time}_#{event}", method_name, options.slice(:if, :on)
38
39
  end
39
40
 
40
41
  def find_by_oauth_credentials(provider, uid)
@@ -10,7 +10,7 @@ module Sorcery
10
10
  attrs[name] = value.utc if value.is_a?(ActiveSupport::TimeWithZone)
11
11
  @model.send(:"#{name}=", value)
12
12
  end
13
- @model.class.where(:_id => @model.id).update_all(attrs)
13
+ @model.class.where(_id: @model.id).update_all(attrs)
14
14
  end
15
15
 
16
16
  def update_attribute(name, value)
@@ -23,21 +23,29 @@ module Sorcery
23
23
  end
24
24
 
25
25
  def mongoid_4?
26
- Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new("4.0.0.alpha")
26
+ Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('4.0.0.alpha')
27
27
  end
28
28
 
29
29
  class << self
30
-
31
- def define_field(name, type, options={})
30
+ def define_field(name, type, options = {})
32
31
  @klass.field name, options.slice(:default).merge(type: type)
33
32
  end
34
33
 
35
- def define_callback(time, event, method_name, options={})
36
- @klass.send "#{time}_#{event}", method_name, options.slice(:if)
34
+ def define_callback(time, event, method_name, options = {})
35
+ @klass.send callback_name(time, event, options), method_name, options.slice(:if)
36
+ end
37
+
38
+ def callback_name(time, event, options)
39
+ if event == :commit
40
+ options[:on] == :create ? "#{time}_create" : "#{time}_save"
41
+ else
42
+ "#{time}_#{event}"
43
+ end
37
44
  end
38
45
 
39
46
  def credential_regex(credential)
40
- return { :$regex => /^#{Regexp.escape(credential)}$/i } if (@klass.sorcery_config.downcase_username_before_authenticating)
47
+ return { :$regex => /^#{Regexp.escape(credential)}$/i } if @klass.sorcery_config.downcase_username_before_authenticating
48
+
41
49
  credential
42
50
  end
43
51
 
@@ -73,7 +81,7 @@ module Sorcery
73
81
  end
74
82
 
75
83
  def find_by_username(username)
76
- query = @klass.sorcery_config.username_attribute_names.map {|name| {name => username}}
84
+ query = @klass.sorcery_config.username_attribute_names.map { |name| { name => username } }
77
85
  @klass.any_of(*query).first
78
86
  end
79
87
 
@@ -87,9 +95,13 @@ module Sorcery
87
95
 
88
96
  def get_current_users
89
97
  config = @klass.sorcery_config
90
- @klass.where(config.last_activity_at_attribute_name.ne => nil) \
91
- .where("this.#{config.last_logout_at_attribute_name} == null || this.#{config.last_activity_at_attribute_name} > this.#{config.last_logout_at_attribute_name}") \
92
- .where(config.last_activity_at_attribute_name.gt => config.activity_timeout.seconds.ago.utc).order_by([:_id,:asc])
98
+ @klass.where(
99
+ config.last_activity_at_attribute_name.ne => nil
100
+ ).where(
101
+ "this.#{config.last_logout_at_attribute_name} == null || this.#{config.last_activity_at_attribute_name} > this.#{config.last_logout_at_attribute_name}"
102
+ ).where(
103
+ config.last_activity_at_attribute_name.gt => config.activity_timeout.seconds.ago.utc
104
+ ).order_by(%i[_id asc])
93
105
  end
94
106
  end
95
107
  end
@@ -4,11 +4,14 @@ module Sorcery
4
4
  klass.class_eval do
5
5
  include InstanceMethods
6
6
  Config.submodules.each do |mod|
7
+ # FIXME: Is there a cleaner way to handle missing submodules?
8
+ # rubocop:disable Lint/HandleExceptions
7
9
  begin
8
10
  include Submodules.const_get(mod.to_s.split('_').map(&:capitalize).join)
9
11
  rescue NameError
10
12
  # don't stop on a missing submodule.
11
13
  end
14
+ # rubocop:enable Lint/HandleExceptions
12
15
  end
13
16
  end
14
17
  Config.update!
@@ -20,10 +23,13 @@ module Sorcery
20
23
  # Will trigger auto-login attempts via the call to logged_in?
21
24
  # If all attempts to auto-login fail, the failure callback will be called.
22
25
  def require_login
23
- unless logged_in?
24
- session[:return_to_url] = request.url if Config.save_return_to_url && request.get? && !request.xhr?
25
- send(Config.not_authenticated_action)
26
+ return if logged_in?
27
+
28
+ if Config.save_return_to_url && request.get? && !request.xhr? && !request.format.json?
29
+ session[:return_to_url] = request.url
26
30
  end
31
+
32
+ send(Config.not_authenticated_action)
27
33
  end
28
34
 
29
35
  # Takes credentials and returns a user on successful authentication.
@@ -37,7 +43,10 @@ module Sorcery
37
43
 
38
44
  yield(user, failure_reason) if block_given?
39
45
 
46
+ # FIXME: Does using `break` or `return nil` change functionality?
47
+ # rubocop:disable Lint/NonLocalExitFromIterator
40
48
  return
49
+ # rubocop:enable Lint/NonLocalExitFromIterator
41
50
  end
42
51
 
43
52
  old_session = session.dup.to_hash
@@ -47,30 +56,26 @@ module Sorcery
47
56
  end
48
57
  form_authenticity_token
49
58
 
50
- auto_login(user)
59
+ auto_login(user, credentials[2])
51
60
  after_login!(user, credentials)
52
61
 
53
62
  block_given? ? yield(current_user, nil) : current_user
54
63
  end
55
64
  end
56
65
 
57
- # put this into the catch block to rescue undefined method `destroy_session'
58
- # hotfix for https://github.com/NoamB/sorcery/issues/464
59
- # can be removed when Rails 4.1 is out
60
66
  def reset_sorcery_session
61
67
  reset_session # protect from session fixation attacks
62
- rescue NoMethodError
63
68
  end
64
69
 
65
70
  # Resets the session and runs hooks before and after.
66
71
  def logout
67
- if logged_in?
68
- user = current_user
69
- before_logout!
70
- @current_user = nil
71
- reset_sorcery_session
72
- after_logout!(user)
73
- end
72
+ return unless logged_in?
73
+
74
+ user = current_user
75
+ before_logout!
76
+ @current_user = nil
77
+ reset_sorcery_session
78
+ after_logout!(user)
74
79
  end
75
80
 
76
81
  def logged_in?
@@ -153,8 +158,14 @@ module Sorcery
153
158
  Config.after_logout.each { |c| send(c, user) }
154
159
  end
155
160
 
161
+ def after_remember_me!(user)
162
+ Config.after_remember_me.each { |c| send(c, user) }
163
+ end
164
+
156
165
  def user_class
157
166
  @user_class ||= Config.user_class.to_s.constantize
167
+ rescue NameError
168
+ raise ArgumentError, 'You have incorrectly defined user_class or have forgotten to define it in intitializer file (config.user_class = \'User\').'
158
169
  end
159
170
  end
160
171
  end
@@ -18,6 +18,7 @@ module Sorcery
18
18
  attr_accessor :after_failed_login
19
19
  attr_accessor :before_logout
20
20
  attr_accessor :after_logout
21
+ attr_accessor :after_remember_me
21
22
 
22
23
  def init!
23
24
  @defaults = {
@@ -29,6 +30,7 @@ module Sorcery
29
30
  :@after_failed_login => [],
30
31
  :@before_logout => [],
31
32
  :@after_logout => [],
33
+ :@after_remember_me => [],
32
34
  :@save_return_to_url => true,
33
35
  :@cookie_domain => nil
34
36
  }
@@ -30,9 +30,16 @@ module Sorcery
30
30
  end
31
31
  merge_activity_logging_defaults!
32
32
  end
33
- Config.after_login << :register_login_time_to_db
34
- Config.after_login << :register_last_ip_address
35
- Config.before_logout << :register_logout_time_to_db
33
+ # FIXME: There is likely a more elegant way to safeguard these callbacks.
34
+ unless Config.after_login.include?(:register_login_time_to_db)
35
+ Config.after_login << :register_login_time_to_db
36
+ end
37
+ unless Config.after_login.include?(:register_last_ip_address)
38
+ Config.after_login << :register_last_ip_address
39
+ end
40
+ unless Config.before_logout.include?(:register_logout_time_to_db)
41
+ Config.before_logout << :register_logout_time_to_db
42
+ end
36
43
  base.after_action :register_last_activity_time_to_db
37
44
  end
38
45
 
@@ -43,6 +50,7 @@ module Sorcery
43
50
  # This runs as a hook just after a successful login.
44
51
  def register_login_time_to_db(user, _credentials)
45
52
  return unless Config.register_login_time
53
+
46
54
  user.set_last_login_at(Time.now.in_time_zone)
47
55
  end
48
56
 
@@ -50,6 +58,7 @@ module Sorcery
50
58
  # This runs as a hook just before a logout.
51
59
  def register_logout_time_to_db
52
60
  return unless Config.register_logout_time
61
+
53
62
  current_user.set_last_logout_at(Time.now.in_time_zone)
54
63
  end
55
64
 
@@ -58,6 +67,7 @@ module Sorcery
58
67
  def register_last_activity_time_to_db
59
68
  return unless Config.register_last_activity_time
60
69
  return unless logged_in?
70
+
61
71
  current_user.set_last_activity_at(Time.now.in_time_zone)
62
72
  end
63
73
 
@@ -65,6 +75,7 @@ module Sorcery
65
75
  # This runs as a hook just after a successful login.
66
76
  def register_last_ip_address(_user, _credentials)
67
77
  return unless Config.register_last_ip_address
78
+
68
79
  current_user.set_last_ip_address(request.remote_ip)
69
80
  end
70
81
  end
@@ -10,9 +10,13 @@ module Sorcery
10
10
  module BruteForceProtection
11
11
  def self.included(base)
12
12
  base.send(:include, InstanceMethods)
13
-
14
- Config.after_login << :reset_failed_logins_count!
15
- Config.after_failed_login << :update_failed_logins_count!
13
+ # FIXME: There is likely a more elegant way to safeguard these callbacks.
14
+ unless Config.after_login.include?(:reset_failed_logins_count!)
15
+ Config.after_login << :reset_failed_logins_count!
16
+ end
17
+ unless Config.after_failed_login.include?(:update_failed_logins_count!)
18
+ Config.after_failed_login << :update_failed_logins_count!
19
+ end
16
20
  end
17
21
 
18
22
  module InstanceMethods
@@ -23,6 +23,10 @@ module Sorcery
23
23
  require 'sorcery/providers/slack'
24
24
  require 'sorcery/providers/wechat'
25
25
  require 'sorcery/providers/microsoft'
26
+ require 'sorcery/providers/instagram'
27
+ require 'sorcery/providers/auth0'
28
+ require 'sorcery/providers/line'
29
+ require 'sorcery/providers/discord'
26
30
 
27
31
  Config.module_eval do
28
32
  class << self
@@ -33,17 +37,17 @@ module Sorcery
33
37
  @external_providers = providers
34
38
 
35
39
  providers.each do |name|
36
- class_eval <<-E
40
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
37
41
  def self.#{name}
38
- @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.capitalize).new
42
+ @#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.classify).new
39
43
  end
40
- E
44
+ RUBY
41
45
  end
42
46
  end
43
47
 
44
48
  def merge_external_defaults!
45
49
  @defaults.merge!(:@external_providers => [],
46
- :@ca_file => File.join(File.expand_path(File.dirname(__FILE__)), '../../protocols/certs/ca-bundle.crt'))
50
+ :@ca_file => File.join(__dir__, '../../protocols/certs/ca-bundle.crt'))
47
51
  end
48
52
  end
49
53
  merge_external_defaults!
@@ -56,6 +60,7 @@ module Sorcery
56
60
  # save the singleton ProviderClient instance into @provider
57
61
  def sorcery_get_provider(provider_name)
58
62
  return unless Config.external_providers.include?(provider_name.to_sym)
63
+
59
64
  Config.send(provider_name.to_sym)
60
65
  end
61
66
 
@@ -64,12 +69,11 @@ module Sorcery
64
69
  def sorcery_login_url(provider_name, args = {})
65
70
  @provider = sorcery_get_provider provider_name
66
71
  sorcery_fixup_callback_url @provider
67
- if @provider.respond_to?(:login_url) && @provider.has_callback?
68
- @provider.state = args[:state]
69
- return @provider.login_url(params, session)
70
- else
71
- return nil
72
- end
72
+
73
+ return nil unless @provider.respond_to?(:login_url) && @provider.has_callback?
74
+
75
+ @provider.state = args[:state]
76
+ @provider.login_url(params, session)
73
77
  end
74
78
 
75
79
  # get the user hash from a provider using information from the params and session.
@@ -88,6 +92,7 @@ module Sorcery
88
92
  # cache them in instance variables.
89
93
  @access_token ||= @provider.process_callback(params, session) # sends request to oauth agent to get the token
90
94
  @user_hash ||= @provider.get_user_hash(@access_token) # uses the token to send another request to the oauth agent requesting user info
95
+ nil
91
96
  end
92
97
 
93
98
  # for backwards compatibility
@@ -98,14 +103,15 @@ module Sorcery
98
103
  # this method should be somewhere else. It only does something once per application per provider.
99
104
  def sorcery_fixup_callback_url(provider)
100
105
  provider.original_callback_url ||= provider.callback_url
101
- if provider.original_callback_url.present? && provider.original_callback_url[0] == '/'
102
- uri = URI.parse(request.url.gsub(/\?.*$/, ''))
103
- uri.path = ''
104
- uri.query = nil
105
- uri.scheme = 'https' if request.env['HTTP_X_FORWARDED_PROTO'] == 'https'
106
- host = uri.to_s
107
- provider.callback_url = "#{host}#{@provider.original_callback_url}"
108
- end
106
+
107
+ return unless provider.original_callback_url.present? && provider.original_callback_url[0] == '/'
108
+
109
+ uri = URI.parse(request.url.gsub(/\?.*$/, ''))
110
+ uri.path = ''
111
+ uri.query = nil
112
+ uri.scheme = 'https' if request.env['HTTP_X_FORWARDED_PROTO'] == 'https'
113
+ host = uri.to_s
114
+ provider.callback_url = "#{host}#{@provider.original_callback_url}"
109
115
  end
110
116
 
111
117
  # sends user to authenticate at the provider's website.
@@ -118,26 +124,26 @@ module Sorcery
118
124
  def login_from(provider_name, should_remember = false)
119
125
  sorcery_fetch_user_hash provider_name
120
126
 
121
- if user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s)
122
- # we found the user.
123
- # clear the session
124
- return_to_url = session[:return_to_url]
125
- reset_sorcery_session
126
- session[:return_to_url] = return_to_url
127
+ return unless (user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s))
127
128
 
128
- # sign in the user
129
- auto_login(user, should_remember)
130
- after_login!(user)
129
+ # we found the user.
130
+ # clear the session
131
+ return_to_url = session[:return_to_url]
132
+ reset_sorcery_session
133
+ session[:return_to_url] = return_to_url
131
134
 
132
- # return the user
133
- user
134
- end
135
+ # sign in the user
136
+ auto_login(user, should_remember)
137
+ after_login!(user)
138
+
139
+ # return the user
140
+ user
135
141
  end
136
142
 
137
143
  # If user is logged, he can add all available providers into his account
138
144
  def add_provider_to_user(provider_name)
139
145
  sorcery_fetch_user_hash provider_name
140
- config = user_class.sorcery_config
146
+ # config = user_class.sorcery_config # TODO: Unused, remove?
141
147
 
142
148
  current_user.add_provider_to_user(provider_name.to_s, @user_hash[:uid].to_s)
143
149
  end
@@ -181,19 +187,28 @@ module Sorcery
181
187
  #
182
188
  def create_from(provider_name, &block)
183
189
  sorcery_fetch_user_hash provider_name
184
- config = user_class.sorcery_config
190
+ # config = user_class.sorcery_config # TODO: Unused, remove?
185
191
 
186
192
  attrs = user_attrs(@provider.user_info_mapping, @user_hash)
187
193
  @user = user_class.create_from_provider(provider_name, @user_hash[:uid], attrs, &block)
188
194
  end
189
195
 
196
+ # follows the same patterns as create_from, but builds the user instead of creating
197
+ def build_from(provider_name, &block)
198
+ sorcery_fetch_user_hash provider_name
199
+ # config = user_class.sorcery_config # TODO: Unused, remove?
200
+
201
+ attrs = user_attrs(@provider.user_info_mapping, @user_hash)
202
+ @user = user_class.build_from_provider(attrs, &block)
203
+ end
204
+
190
205
  def user_attrs(user_info_mapping, user_hash)
191
206
  attrs = {}
192
207
  user_info_mapping.each do |k, v|
193
208
  if (varr = v.split('/')).size > 1
194
209
  attribute_value = begin
195
210
  varr.inject(user_hash[:user_info]) { |hash, value| hash[value] }
196
- rescue
211
+ rescue StandardError
197
212
  nil
198
213
  end
199
214
  attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)