sorcery 0.11.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE.md +20 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- data/.github/workflows/ruby.yml +23 -0
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +155 -0
- data/.travis.yml +11 -51
- data/CHANGELOG.md +75 -0
- data/CODE_OF_CONDUCT.md +14 -0
- data/Gemfile +2 -2
- data/{LICENSE.txt → LICENSE.md} +1 -1
- data/README.md +34 -7
- data/SECURITY.md +18 -0
- data/gemfiles/rails_52.gemfile +7 -0
- data/gemfiles/rails_60.gemfile +7 -0
- data/lib/generators/sorcery/USAGE +1 -1
- data/lib/generators/sorcery/helpers.rb +4 -0
- data/lib/generators/sorcery/install_generator.rb +21 -21
- data/lib/generators/sorcery/templates/initializer.rb +176 -69
- data/lib/generators/sorcery/templates/migration/activity_logging.rb +5 -5
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +4 -4
- data/lib/generators/sorcery/templates/migration/core.rb +4 -4
- data/lib/generators/sorcery/templates/migration/external.rb +3 -3
- data/lib/generators/sorcery/templates/migration/magic_login.rb +9 -0
- data/lib/generators/sorcery/templates/migration/remember_me.rb +3 -3
- data/lib/generators/sorcery/templates/migration/reset_password.rb +5 -4
- data/lib/generators/sorcery/templates/migration/user_activation.rb +4 -4
- data/lib/sorcery.rb +2 -0
- data/lib/sorcery/adapters/active_record_adapter.rb +4 -3
- data/lib/sorcery/adapters/mongoid_adapter.rb +23 -11
- data/lib/sorcery/controller.rb +26 -15
- data/lib/sorcery/controller/config.rb +7 -5
- data/lib/sorcery/controller/submodules/activity_logging.rb +9 -3
- data/lib/sorcery/controller/submodules/external.rb +52 -33
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +2 -0
- data/lib/sorcery/controller/submodules/remember_me.rb +3 -8
- data/lib/sorcery/controller/submodules/session_timeout.rb +28 -5
- data/lib/sorcery/crypto_providers/aes256.rb +2 -1
- data/lib/sorcery/crypto_providers/bcrypt.rb +8 -2
- data/lib/sorcery/engine.rb +16 -3
- data/lib/sorcery/model.rb +14 -10
- data/lib/sorcery/model/config.rb +12 -4
- data/lib/sorcery/model/submodules/brute_force_protection.rb +6 -7
- data/lib/sorcery/model/submodules/external.rb +19 -3
- data/lib/sorcery/model/submodules/magic_login.rb +130 -0
- data/lib/sorcery/model/submodules/reset_password.rb +25 -2
- data/lib/sorcery/model/submodules/user_activation.rb +1 -1
- data/lib/sorcery/model/temporary_token.rb +3 -1
- data/lib/sorcery/protocols/oauth.rb +1 -0
- data/lib/sorcery/providers/auth0.rb +46 -0
- data/lib/sorcery/providers/battlenet.rb +51 -0
- data/lib/sorcery/providers/discord.rb +52 -0
- data/lib/sorcery/providers/heroku.rb +1 -0
- data/lib/sorcery/providers/instagram.rb +73 -0
- data/lib/sorcery/providers/line.rb +63 -0
- data/lib/sorcery/providers/linkedin.rb +45 -36
- data/lib/sorcery/providers/vk.rb +5 -4
- data/lib/sorcery/providers/wechat.rb +8 -6
- data/lib/sorcery/test_helpers/internal.rb +5 -4
- data/lib/sorcery/test_helpers/internal/rails.rb +11 -11
- data/lib/sorcery/test_helpers/rails/request.rb +20 -0
- data/lib/sorcery/version.rb +1 -1
- data/sorcery.gemspec +26 -10
- data/spec/active_record/user_activation_spec.rb +2 -2
- data/spec/active_record/user_activity_logging_spec.rb +2 -2
- data/spec/active_record/user_brute_force_protection_spec.rb +2 -2
- data/spec/active_record/user_magic_login_spec.rb +15 -0
- data/spec/active_record/user_oauth_spec.rb +2 -2
- data/spec/active_record/user_remember_me_spec.rb +2 -2
- data/spec/active_record/user_reset_password_spec.rb +2 -2
- data/spec/active_record/user_spec.rb +0 -10
- data/spec/controllers/controller_http_basic_auth_spec.rb +1 -1
- data/spec/controllers/controller_oauth2_spec.rb +230 -123
- data/spec/controllers/controller_oauth_spec.rb +13 -7
- data/spec/controllers/controller_remember_me_spec.rb +16 -8
- data/spec/controllers/controller_session_timeout_spec.rb +90 -3
- data/spec/controllers/controller_spec.rb +13 -3
- data/spec/orm/active_record.rb +2 -2
- data/spec/providers/example_provider_spec.rb +17 -0
- data/spec/providers/example_spec.rb +17 -0
- data/spec/providers/vk_spec.rb +42 -0
- data/spec/rails_app/app/assets/config/manifest.js +1 -0
- data/spec/rails_app/app/controllers/application_controller.rb +2 -0
- data/spec/rails_app/app/controllers/sorcery_controller.rb +152 -33
- data/spec/rails_app/app/mailers/sorcery_mailer.rb +7 -0
- data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb +13 -0
- data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb +6 -0
- data/spec/rails_app/config/application.rb +8 -3
- data/spec/rails_app/config/boot.rb +1 -1
- data/spec/rails_app/config/environment.rb +1 -1
- data/spec/rails_app/config/routes.rb +17 -0
- data/spec/rails_app/config/secrets.yml +4 -0
- data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +2 -2
- data/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb +9 -0
- data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +17 -0
- data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +2 -0
- data/spec/rails_app/db/schema.rb +7 -9
- data/spec/shared_examples/user_magic_login_shared_examples.rb +150 -0
- data/spec/shared_examples/user_oauth_shared_examples.rb +1 -1
- data/spec/shared_examples/user_remember_me_shared_examples.rb +1 -1
- data/spec/shared_examples/user_reset_password_shared_examples.rb +37 -5
- data/spec/shared_examples/user_shared_examples.rb +104 -43
- data/spec/sorcery_crypto_providers_spec.rb +61 -1
- data/spec/sorcery_temporary_token_spec.rb +27 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +2 -2
- data/spec/support/migration_helper.rb +29 -0
- data/spec/support/providers/example.rb +11 -0
- data/spec/support/providers/example_provider.rb +11 -0
- metadata +92 -29
- data/gemfiles/active_record-rails40.gemfile +0 -7
- data/gemfiles/active_record-rails41.gemfile +0 -7
- data/gemfiles/active_record-rails42.gemfile +0 -7
- data/spec/rails_app/config/initializers/secret_token.rb +0 -7
@@ -1,10 +1,10 @@
|
|
1
1
|
class SorceryActivityLogging < <%= migration_class_name %>
|
2
2
|
def change
|
3
|
-
add_column :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
6
|
-
add_column :<%=
|
3
|
+
add_column :<%= tableized_model_class %>, :last_login_at, :datetime, default: nil
|
4
|
+
add_column :<%= tableized_model_class %>, :last_logout_at, :datetime, default: nil
|
5
|
+
add_column :<%= tableized_model_class %>, :last_activity_at, :datetime, default: nil
|
6
|
+
add_column :<%= tableized_model_class %>, :last_login_from_ip_address, :string, default: nil
|
7
7
|
|
8
|
-
add_index :<%=
|
8
|
+
add_index :<%= tableized_model_class %>, [:last_logout_at, :last_activity_at]
|
9
9
|
end
|
10
10
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class SorceryBruteForceProtection < <%= migration_class_name %>
|
2
2
|
def change
|
3
|
-
add_column :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
3
|
+
add_column :<%= tableized_model_class %>, :failed_logins_count, :integer, default: 0
|
4
|
+
add_column :<%= tableized_model_class %>, :lock_expires_at, :datetime, default: nil
|
5
|
+
add_column :<%= tableized_model_class %>, :unlock_token, :string, default: nil
|
6
6
|
|
7
|
-
add_index :<%=
|
7
|
+
add_index :<%= tableized_model_class %>, :unlock_token
|
8
8
|
end
|
9
9
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
class SorceryCore < <%= migration_class_name %>
|
2
2
|
def change
|
3
|
-
create_table :<%=
|
4
|
-
t.string :email, :
|
3
|
+
create_table :<%= tableized_model_class %> do |t|
|
4
|
+
t.string :email, null: false
|
5
5
|
t.string :crypted_password
|
6
6
|
t.string :salt
|
7
7
|
|
8
|
-
t.timestamps :
|
8
|
+
t.timestamps null: false
|
9
9
|
end
|
10
10
|
|
11
|
-
add_index :<%=
|
11
|
+
add_index :<%= tableized_model_class %>, :email, unique: true
|
12
12
|
end
|
13
13
|
end
|
@@ -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 :<%=
|
5
|
-
t.string :provider, :uid, :
|
4
|
+
t.integer :<%= tableized_model_class.singularize %>_id, null: false
|
5
|
+
t.string :provider, :uid, null: false
|
6
6
|
|
7
|
-
t.timestamps :
|
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 :<%= tableized_model_class %>, :magic_login_token, :string, default: nil
|
4
|
+
add_column :<%= tableized_model_class %>, :magic_login_token_expires_at, :datetime, default: nil
|
5
|
+
add_column :<%= tableized_model_class %>, :magic_login_email_sent_at, :datetime, default: nil
|
6
|
+
|
7
|
+
add_index :<%= tableized_model_class %>, :magic_login_token
|
8
|
+
end
|
9
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class SorceryRememberMe < <%= migration_class_name %>
|
2
2
|
def change
|
3
|
-
add_column :<%=
|
4
|
-
add_column :<%=
|
3
|
+
add_column :<%= tableized_model_class %>, :remember_me_token, :string, default: nil
|
4
|
+
add_column :<%= tableized_model_class %>, :remember_me_token_expires_at, :datetime, default: nil
|
5
5
|
|
6
|
-
add_index :<%=
|
6
|
+
add_index :<%= tableized_model_class %>, :remember_me_token
|
7
7
|
end
|
8
8
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
class SorceryResetPassword < <%= migration_class_name %>
|
2
2
|
def change
|
3
|
-
add_column :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
3
|
+
add_column :<%= tableized_model_class %>, :reset_password_token, :string, default: nil
|
4
|
+
add_column :<%= tableized_model_class %>, :reset_password_token_expires_at, :datetime, default: nil
|
5
|
+
add_column :<%= tableized_model_class %>, :reset_password_email_sent_at, :datetime, default: nil
|
6
|
+
add_column :<%= tableized_model_class %>, :access_count_to_reset_password_page, :integer, default: 0
|
6
7
|
|
7
|
-
add_index :<%=
|
8
|
+
add_index :<%= tableized_model_class %>, :reset_password_token
|
8
9
|
end
|
9
10
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class SorceryUserActivation < <%= migration_class_name %>
|
2
2
|
def change
|
3
|
-
add_column :<%=
|
4
|
-
add_column :<%=
|
5
|
-
add_column :<%=
|
3
|
+
add_column :<%= tableized_model_class %>, :activation_state, :string, default: nil
|
4
|
+
add_column :<%= tableized_model_class %>, :activation_token, :string, default: nil
|
5
|
+
add_column :<%= tableized_model_class %>, :activation_token_expires_at, :datetime, default: nil
|
6
6
|
|
7
|
-
add_index :<%=
|
7
|
+
add_index :<%= tableized_model_class %>, :activation_token
|
8
8
|
end
|
9
9
|
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,12 +6,13 @@ 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 = {})
|
13
14
|
mthd = options.delete(:raise_on_failure) ? :save! : :save
|
14
|
-
@model.send(mthd, options)
|
15
|
+
@model.send(mthd, **options)
|
15
16
|
end
|
16
17
|
|
17
18
|
def increment(field)
|
@@ -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(:
|
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(
|
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
|
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 =>
|
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(
|
91
|
-
|
92
|
-
.where(
|
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
|
data/lib/sorcery/controller.rb
CHANGED
@@ -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
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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,17 +18,19 @@ 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 = {
|
24
25
|
:@user_class => nil,
|
25
26
|
:@submodules => [],
|
26
27
|
:@not_authenticated_action => :not_authenticated,
|
27
|
-
:@login_sources =>
|
28
|
-
:@after_login =>
|
29
|
-
:@after_failed_login =>
|
30
|
-
:@before_logout =>
|
31
|
-
:@after_logout =>
|
28
|
+
:@login_sources => Set.new,
|
29
|
+
:@after_login => Set.new,
|
30
|
+
:@after_failed_login => Set.new,
|
31
|
+
:@before_logout => Set.new,
|
32
|
+
:@after_logout => Set.new,
|
33
|
+
:@after_remember_me => Set.new,
|
32
34
|
:@save_return_to_url => true,
|
33
35
|
:@cookie_domain => nil
|
34
36
|
}
|
@@ -30,9 +30,11 @@ module Sorcery
|
|
30
30
|
end
|
31
31
|
merge_activity_logging_defaults!
|
32
32
|
end
|
33
|
-
|
34
|
-
Config.after_login
|
35
|
-
Config.
|
33
|
+
|
34
|
+
Config.after_login << :register_login_time_to_db
|
35
|
+
Config.after_login << :register_last_ip_address
|
36
|
+
Config.before_logout << :register_logout_time_to_db
|
37
|
+
|
36
38
|
base.after_action :register_last_activity_time_to_db
|
37
39
|
end
|
38
40
|
|
@@ -43,6 +45,7 @@ module Sorcery
|
|
43
45
|
# This runs as a hook just after a successful login.
|
44
46
|
def register_login_time_to_db(user, _credentials)
|
45
47
|
return unless Config.register_login_time
|
48
|
+
|
46
49
|
user.set_last_login_at(Time.now.in_time_zone)
|
47
50
|
end
|
48
51
|
|
@@ -50,6 +53,7 @@ module Sorcery
|
|
50
53
|
# This runs as a hook just before a logout.
|
51
54
|
def register_logout_time_to_db
|
52
55
|
return unless Config.register_logout_time
|
56
|
+
|
53
57
|
current_user.set_last_logout_at(Time.now.in_time_zone)
|
54
58
|
end
|
55
59
|
|
@@ -58,6 +62,7 @@ module Sorcery
|
|
58
62
|
def register_last_activity_time_to_db
|
59
63
|
return unless Config.register_last_activity_time
|
60
64
|
return unless logged_in?
|
65
|
+
|
61
66
|
current_user.set_last_activity_at(Time.now.in_time_zone)
|
62
67
|
end
|
63
68
|
|
@@ -65,6 +70,7 @@ module Sorcery
|
|
65
70
|
# This runs as a hook just after a successful login.
|
66
71
|
def register_last_ip_address(_user, _credentials)
|
67
72
|
return unless Config.register_last_ip_address
|
73
|
+
|
68
74
|
current_user.set_last_ip_address(request.remote_ip)
|
69
75
|
end
|
70
76
|
end
|
@@ -23,6 +23,14 @@ 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'
|
30
|
+
require 'sorcery/providers/battlenet'
|
31
|
+
|
32
|
+
|
33
|
+
|
26
34
|
|
27
35
|
Config.module_eval do
|
28
36
|
class << self
|
@@ -33,17 +41,17 @@ module Sorcery
|
|
33
41
|
@external_providers = providers
|
34
42
|
|
35
43
|
providers.each do |name|
|
36
|
-
class_eval <<-
|
44
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
37
45
|
def self.#{name}
|
38
|
-
@#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.
|
46
|
+
@#{name} ||= Sorcery::Providers.const_get('#{name}'.to_s.classify).new
|
39
47
|
end
|
40
|
-
|
48
|
+
RUBY
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
44
52
|
def merge_external_defaults!
|
45
53
|
@defaults.merge!(:@external_providers => [],
|
46
|
-
:@ca_file => File.join(
|
54
|
+
:@ca_file => File.join(__dir__, '../../protocols/certs/ca-bundle.crt'))
|
47
55
|
end
|
48
56
|
end
|
49
57
|
merge_external_defaults!
|
@@ -56,6 +64,7 @@ module Sorcery
|
|
56
64
|
# save the singleton ProviderClient instance into @provider
|
57
65
|
def sorcery_get_provider(provider_name)
|
58
66
|
return unless Config.external_providers.include?(provider_name.to_sym)
|
67
|
+
|
59
68
|
Config.send(provider_name.to_sym)
|
60
69
|
end
|
61
70
|
|
@@ -64,12 +73,11 @@ module Sorcery
|
|
64
73
|
def sorcery_login_url(provider_name, args = {})
|
65
74
|
@provider = sorcery_get_provider provider_name
|
66
75
|
sorcery_fixup_callback_url @provider
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
76
|
+
|
77
|
+
return nil unless @provider.respond_to?(:login_url) && @provider.has_callback?
|
78
|
+
|
79
|
+
@provider.state = args[:state]
|
80
|
+
@provider.login_url(params, session)
|
73
81
|
end
|
74
82
|
|
75
83
|
# get the user hash from a provider using information from the params and session.
|
@@ -88,6 +96,7 @@ module Sorcery
|
|
88
96
|
# cache them in instance variables.
|
89
97
|
@access_token ||= @provider.process_callback(params, session) # sends request to oauth agent to get the token
|
90
98
|
@user_hash ||= @provider.get_user_hash(@access_token) # uses the token to send another request to the oauth agent requesting user info
|
99
|
+
nil
|
91
100
|
end
|
92
101
|
|
93
102
|
# for backwards compatibility
|
@@ -98,14 +107,15 @@ module Sorcery
|
|
98
107
|
# this method should be somewhere else. It only does something once per application per provider.
|
99
108
|
def sorcery_fixup_callback_url(provider)
|
100
109
|
provider.original_callback_url ||= provider.callback_url
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
110
|
+
|
111
|
+
return unless provider.original_callback_url.present? && provider.original_callback_url[0] == '/'
|
112
|
+
|
113
|
+
uri = URI.parse(request.url.gsub(/\?.*$/, ''))
|
114
|
+
uri.path = ''
|
115
|
+
uri.query = nil
|
116
|
+
uri.scheme = 'https' if request.env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
117
|
+
host = uri.to_s
|
118
|
+
provider.callback_url = "#{host}#{@provider.original_callback_url}"
|
109
119
|
end
|
110
120
|
|
111
121
|
# sends user to authenticate at the provider's website.
|
@@ -118,26 +128,26 @@ module Sorcery
|
|
118
128
|
def login_from(provider_name, should_remember = false)
|
119
129
|
sorcery_fetch_user_hash provider_name
|
120
130
|
|
121
|
-
|
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
|
131
|
+
return unless (user = user_class.load_from_provider(provider_name, @user_hash[:uid].to_s))
|
127
132
|
|
128
|
-
|
129
|
-
|
130
|
-
|
133
|
+
# we found the user.
|
134
|
+
# clear the session
|
135
|
+
return_to_url = session[:return_to_url]
|
136
|
+
reset_sorcery_session
|
137
|
+
session[:return_to_url] = return_to_url
|
131
138
|
|
132
|
-
|
133
|
-
|
134
|
-
|
139
|
+
# sign in the user
|
140
|
+
auto_login(user, should_remember)
|
141
|
+
after_login!(user)
|
142
|
+
|
143
|
+
# return the user
|
144
|
+
user
|
135
145
|
end
|
136
146
|
|
137
147
|
# If user is logged, he can add all available providers into his account
|
138
148
|
def add_provider_to_user(provider_name)
|
139
149
|
sorcery_fetch_user_hash provider_name
|
140
|
-
config = user_class.sorcery_config
|
150
|
+
# config = user_class.sorcery_config # TODO: Unused, remove?
|
141
151
|
|
142
152
|
current_user.add_provider_to_user(provider_name.to_s, @user_hash[:uid].to_s)
|
143
153
|
end
|
@@ -181,19 +191,28 @@ module Sorcery
|
|
181
191
|
#
|
182
192
|
def create_from(provider_name, &block)
|
183
193
|
sorcery_fetch_user_hash provider_name
|
184
|
-
config = user_class.sorcery_config
|
194
|
+
# config = user_class.sorcery_config # TODO: Unused, remove?
|
185
195
|
|
186
196
|
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
|
187
197
|
@user = user_class.create_from_provider(provider_name, @user_hash[:uid], attrs, &block)
|
188
198
|
end
|
189
199
|
|
200
|
+
# follows the same patterns as create_from, but builds the user instead of creating
|
201
|
+
def build_from(provider_name, &block)
|
202
|
+
sorcery_fetch_user_hash provider_name
|
203
|
+
# config = user_class.sorcery_config # TODO: Unused, remove?
|
204
|
+
|
205
|
+
attrs = user_attrs(@provider.user_info_mapping, @user_hash)
|
206
|
+
@user = user_class.build_from_provider(attrs, &block)
|
207
|
+
end
|
208
|
+
|
190
209
|
def user_attrs(user_info_mapping, user_hash)
|
191
210
|
attrs = {}
|
192
211
|
user_info_mapping.each do |k, v|
|
193
212
|
if (varr = v.split('/')).size > 1
|
194
213
|
attribute_value = begin
|
195
214
|
varr.inject(user_hash[:user_info]) { |hash, value| hash[value] }
|
196
|
-
rescue
|
215
|
+
rescue StandardError
|
197
216
|
nil
|
198
217
|
end
|
199
218
|
attribute_value.nil? ? attrs : attrs.merge!(k => attribute_value)
|