sorcery 0.9.1 → 0.16.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. checksums.yaml +5 -5
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/ISSUE_TEMPLATE.md +24 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +7 -0
  5. data/.github/workflows/ruby.yml +70 -0
  6. data/.gitignore +3 -0
  7. data/.rubocop.yml +55 -0
  8. data/.rubocop_todo.yml +163 -0
  9. data/CHANGELOG.md +132 -34
  10. data/CODE_OF_CONDUCT.md +14 -0
  11. data/Gemfile +3 -17
  12. data/{LICENSE.txt → LICENSE.md} +1 -1
  13. data/MAINTAINING.md +64 -0
  14. data/README.md +146 -269
  15. data/Rakefile +4 -2
  16. data/SECURITY.md +19 -0
  17. data/gemfiles/rails_52.gemfile +7 -0
  18. data/gemfiles/rails_60.gemfile +7 -0
  19. data/gemfiles/rails_61.gemfile +7 -0
  20. data/gemfiles/rails_70.gemfile +7 -0
  21. data/lib/generators/sorcery/USAGE +1 -1
  22. data/lib/generators/sorcery/helpers.rb +8 -4
  23. data/lib/generators/sorcery/install_generator.rb +41 -35
  24. data/lib/generators/sorcery/templates/initializer.rb +216 -112
  25. data/lib/generators/sorcery/templates/migration/activity_logging.rb +7 -7
  26. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +5 -5
  27. data/lib/generators/sorcery/templates/migration/core.rb +5 -7
  28. data/lib/generators/sorcery/templates/migration/external.rb +4 -4
  29. data/lib/generators/sorcery/templates/migration/magic_login.rb +9 -0
  30. data/lib/generators/sorcery/templates/migration/remember_me.rb +5 -5
  31. data/lib/generators/sorcery/templates/migration/reset_password.rb +7 -6
  32. data/lib/generators/sorcery/templates/migration/user_activation.rb +6 -6
  33. data/lib/sorcery/adapters/active_record_adapter.rb +11 -21
  34. data/lib/sorcery/adapters/mongoid_adapter.rb +23 -11
  35. data/lib/sorcery/controller/config.rb +27 -23
  36. data/lib/sorcery/controller/submodules/activity_logging.rb +16 -18
  37. data/lib/sorcery/controller/submodules/brute_force_protection.rb +1 -2
  38. data/lib/sorcery/controller/submodules/external.rb +69 -44
  39. data/lib/sorcery/controller/submodules/http_basic_auth.rb +18 -19
  40. data/lib/sorcery/controller/submodules/remember_me.rb +16 -16
  41. data/lib/sorcery/controller/submodules/session_timeout.rb +33 -11
  42. data/lib/sorcery/controller.rb +50 -35
  43. data/lib/sorcery/crypto_providers/aes256.rb +17 -16
  44. data/lib/sorcery/crypto_providers/bcrypt.rb +26 -22
  45. data/lib/sorcery/crypto_providers/common.rb +1 -1
  46. data/lib/sorcery/crypto_providers/md5.rb +5 -5
  47. data/lib/sorcery/crypto_providers/sha1.rb +5 -5
  48. data/lib/sorcery/crypto_providers/sha256.rb +2 -2
  49. data/lib/sorcery/crypto_providers/sha512.rb +3 -3
  50. data/lib/sorcery/engine.rb +19 -11
  51. data/lib/sorcery/model/config.rb +73 -50
  52. data/lib/sorcery/model/submodules/activity_logging.rb +31 -12
  53. data/lib/sorcery/model/submodules/brute_force_protection.rb +38 -31
  54. data/lib/sorcery/model/submodules/external.rb +22 -10
  55. data/lib/sorcery/model/submodules/magic_login.rb +130 -0
  56. data/lib/sorcery/model/submodules/remember_me.rb +19 -7
  57. data/lib/sorcery/model/submodules/reset_password.rb +64 -42
  58. data/lib/sorcery/model/submodules/user_activation.rb +52 -54
  59. data/lib/sorcery/model/temporary_token.rb +30 -7
  60. data/lib/sorcery/model.rb +65 -40
  61. data/lib/sorcery/protocols/oauth.rb +4 -9
  62. data/lib/sorcery/protocols/oauth2.rb +0 -2
  63. data/lib/sorcery/providers/auth0.rb +46 -0
  64. data/lib/sorcery/providers/base.rb +4 -4
  65. data/lib/sorcery/providers/battlenet.rb +51 -0
  66. data/lib/sorcery/providers/discord.rb +52 -0
  67. data/lib/sorcery/providers/facebook.rb +8 -11
  68. data/lib/sorcery/providers/github.rb +5 -7
  69. data/lib/sorcery/providers/google.rb +3 -5
  70. data/lib/sorcery/providers/heroku.rb +7 -8
  71. data/lib/sorcery/providers/instagram.rb +73 -0
  72. data/lib/sorcery/providers/jira.rb +12 -17
  73. data/lib/sorcery/providers/line.rb +63 -0
  74. data/lib/sorcery/providers/linkedin.rb +44 -35
  75. data/lib/sorcery/providers/liveid.rb +4 -7
  76. data/lib/sorcery/providers/microsoft.rb +59 -0
  77. data/lib/sorcery/providers/paypal.rb +60 -0
  78. data/lib/sorcery/providers/salesforce.rb +3 -5
  79. data/lib/sorcery/providers/slack.rb +45 -0
  80. data/lib/sorcery/providers/twitter.rb +4 -6
  81. data/lib/sorcery/providers/vk.rb +8 -9
  82. data/lib/sorcery/providers/wechat.rb +81 -0
  83. data/lib/sorcery/providers/xing.rb +7 -10
  84. data/lib/sorcery/test_helpers/internal/rails.rb +25 -17
  85. data/lib/sorcery/test_helpers/internal.rb +15 -14
  86. data/lib/sorcery/test_helpers/rails/controller.rb +1 -1
  87. data/lib/sorcery/test_helpers/rails/integration.rb +5 -6
  88. data/lib/sorcery/test_helpers/rails/request.rb +20 -0
  89. data/lib/sorcery/version.rb +1 -1
  90. data/lib/sorcery.rb +4 -17
  91. data/sorcery.gemspec +43 -28
  92. data/spec/active_record/user_activation_spec.rb +4 -5
  93. data/spec/active_record/user_activity_logging_spec.rb +4 -6
  94. data/spec/active_record/user_brute_force_protection_spec.rb +5 -6
  95. data/spec/active_record/user_magic_login_spec.rb +15 -0
  96. data/spec/active_record/user_oauth_spec.rb +5 -6
  97. data/spec/active_record/user_remember_me_spec.rb +5 -6
  98. data/spec/active_record/user_reset_password_spec.rb +4 -5
  99. data/spec/active_record/user_spec.rb +7 -17
  100. data/spec/controllers/controller_activity_logging_spec.rb +13 -24
  101. data/spec/controllers/controller_brute_force_protection_spec.rb +8 -10
  102. data/spec/controllers/controller_http_basic_auth_spec.rb +20 -21
  103. data/spec/controllers/controller_oauth2_spec.rb +297 -158
  104. data/spec/controllers/controller_oauth_spec.rb +97 -71
  105. data/spec/controllers/controller_remember_me_spec.rb +49 -36
  106. data/spec/controllers/controller_session_timeout_spec.rb +106 -20
  107. data/spec/controllers/controller_spec.rb +87 -111
  108. data/spec/orm/active_record.rb +3 -3
  109. data/spec/providers/example_provider_spec.rb +17 -0
  110. data/spec/providers/example_spec.rb +17 -0
  111. data/spec/providers/examples_spec.rb +17 -0
  112. data/spec/providers/vk_spec.rb +42 -0
  113. data/spec/rails_app/app/active_record/authentication.rb +1 -1
  114. data/spec/rails_app/app/active_record/user.rb +2 -2
  115. data/spec/rails_app/app/assets/config/manifest.js +1 -0
  116. data/spec/rails_app/app/controllers/application_controller.rb +2 -0
  117. data/spec/rails_app/app/controllers/sorcery_controller.rb +250 -46
  118. data/spec/rails_app/app/mailers/sorcery_mailer.rb +23 -17
  119. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb +13 -0
  120. data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb +6 -0
  121. data/spec/rails_app/config/application.rb +14 -9
  122. data/spec/rails_app/config/boot.rb +2 -2
  123. data/spec/rails_app/config/environment.rb +1 -1
  124. data/spec/rails_app/config/environments/test.rb +1 -1
  125. data/spec/rails_app/config/initializers/compatible_legacy_migration.rb +11 -0
  126. data/spec/rails_app/config/initializers/session_store.rb +3 -3
  127. data/spec/rails_app/config/routes.rb +31 -1
  128. data/spec/rails_app/config/secrets.yml +4 -0
  129. data/spec/rails_app/config.ru +1 -1
  130. data/spec/rails_app/db/migrate/activation/20101224223622_add_activation_to_users.rb +4 -4
  131. data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +10 -10
  132. data/spec/rails_app/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +5 -5
  133. data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +5 -5
  134. data/spec/rails_app/db/migrate/external/20101224223628_create_authentications_and_user_providers.rb +3 -3
  135. data/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb +9 -0
  136. data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +17 -0
  137. data/spec/rails_app/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +6 -6
  138. data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +7 -5
  139. data/spec/rails_app/db/schema.rb +7 -9
  140. data/spec/shared_examples/user_activation_shared_examples.rb +177 -58
  141. data/spec/shared_examples/user_activity_logging_shared_examples.rb +47 -41
  142. data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +19 -24
  143. data/spec/shared_examples/user_magic_login_shared_examples.rb +150 -0
  144. data/spec/shared_examples/user_oauth_shared_examples.rb +7 -10
  145. data/spec/shared_examples/user_remember_me_shared_examples.rb +91 -22
  146. data/spec/shared_examples/user_reset_password_shared_examples.rb +153 -58
  147. data/spec/shared_examples/user_shared_examples.rb +328 -145
  148. data/spec/sorcery_crypto_providers_spec.rb +122 -75
  149. data/spec/sorcery_temporary_token_spec.rb +27 -0
  150. data/spec/spec.opts +1 -1
  151. data/spec/spec_helper.rb +19 -14
  152. data/spec/support/migration_helper.rb +29 -0
  153. data/spec/support/providers/example.rb +11 -0
  154. data/spec/support/providers/example_provider.rb +11 -0
  155. data/spec/support/providers/examples.rb +11 -0
  156. metadata +119 -89
  157. data/.travis.yml +0 -132
  158. data/gemfiles/active_record-rails40.gemfile +0 -7
  159. data/gemfiles/active_record-rails41.gemfile +0 -7
  160. data/gemfiles/mongo_mapper-rails40.gemfile +0 -9
  161. data/gemfiles/mongo_mapper-rails41.gemfile +0 -9
  162. data/gemfiles/mongoid-rails40.gemfile +0 -9
  163. data/gemfiles/mongoid-rails41.gemfile +0 -9
  164. data/gemfiles/mongoid3-rails32.gemfile +0 -9
  165. data/lib/sorcery/adapters/data_mapper_adapter.rb +0 -176
  166. data/lib/sorcery/adapters/mongo_mapper_adapter.rb +0 -110
  167. data/lib/sorcery/railties/tasks.rake +0 -6
  168. data/spec/data_mapper/user_activation_spec.rb +0 -10
  169. data/spec/data_mapper/user_activity_logging_spec.rb +0 -14
  170. data/spec/data_mapper/user_brute_force_protection_spec.rb +0 -9
  171. data/spec/data_mapper/user_oauth_spec.rb +0 -9
  172. data/spec/data_mapper/user_remember_me_spec.rb +0 -8
  173. data/spec/data_mapper/user_reset_password_spec.rb +0 -8
  174. data/spec/data_mapper/user_spec.rb +0 -27
  175. data/spec/mongo_mapper/user_activation_spec.rb +0 -9
  176. data/spec/mongo_mapper/user_activity_logging_spec.rb +0 -8
  177. data/spec/mongo_mapper/user_brute_force_protection_spec.rb +0 -8
  178. data/spec/mongo_mapper/user_oauth_spec.rb +0 -8
  179. data/spec/mongo_mapper/user_remember_me_spec.rb +0 -8
  180. data/spec/mongo_mapper/user_reset_password_spec.rb +0 -8
  181. data/spec/mongo_mapper/user_spec.rb +0 -37
  182. data/spec/mongoid/user_activation_spec.rb +0 -9
  183. data/spec/mongoid/user_activity_logging_spec.rb +0 -8
  184. data/spec/mongoid/user_brute_force_protection_spec.rb +0 -8
  185. data/spec/mongoid/user_oauth_spec.rb +0 -8
  186. data/spec/mongoid/user_remember_me_spec.rb +0 -8
  187. data/spec/mongoid/user_reset_password_spec.rb +0 -8
  188. data/spec/mongoid/user_spec.rb +0 -51
  189. data/spec/orm/data_mapper.rb +0 -48
  190. data/spec/orm/mongo_mapper.rb +0 -10
  191. data/spec/orm/mongoid.rb +0 -22
  192. data/spec/rails_app/app/data_mapper/authentication.rb +0 -8
  193. data/spec/rails_app/app/data_mapper/user.rb +0 -7
  194. data/spec/rails_app/app/mongo_mapper/authentication.rb +0 -6
  195. data/spec/rails_app/app/mongo_mapper/user.rb +0 -7
  196. data/spec/rails_app/app/mongoid/authentication.rb +0 -7
  197. data/spec/rails_app/app/mongoid/user.rb +0 -7
  198. data/spec/rails_app/config/initializers/secret_token.rb +0 -7
  199. data/spec/rails_app/log/development.log +0 -1791
@@ -6,16 +6,24 @@ module Sorcery
6
6
  # With the plugin logic.
7
7
  class Engine < Rails::Engine
8
8
  config.sorcery = ::Sorcery::Controller::Config
9
-
10
- initializer "extend Controller with sorcery" do |app|
11
- ActionController::Base.send(:include, Sorcery::Controller)
12
- ActionController::Base.helper_method :current_user
13
- ActionController::Base.helper_method :logged_in?
14
- end
15
-
16
- rake_tasks do
17
- load "sorcery/railties/tasks.rake"
9
+
10
+ # TODO: Should this include a modified version of the helper methods?
11
+ initializer 'extend Controller with sorcery' do
12
+ # FIXME: on_load is needed to fix Rails 6 deprecations, but it breaks
13
+ # applications due to undefined method errors.
14
+ # ActiveSupport.on_load(:action_controller_api) do
15
+ if defined?(ActionController::API)
16
+ ActionController::API.send(:include, Sorcery::Controller)
17
+ end
18
+
19
+ # FIXME: on_load is needed to fix Rails 6 deprecations, but it breaks
20
+ # applications due to undefined method errors.
21
+ # ActiveSupport.on_load(:action_controller_base) do
22
+ if defined?(ActionController::Base)
23
+ ActionController::Base.send(:include, Sorcery::Controller)
24
+ ActionController::Base.helper_method :current_user
25
+ ActionController::Base.helper_method :logged_in?
26
+ end
18
27
  end
19
-
20
28
  end
21
- end
29
+ end
@@ -4,43 +4,55 @@
4
4
  module Sorcery
5
5
  module Model
6
6
  class Config
7
-
8
- attr_accessor :username_attribute_names, # change default username attribute, for example, to use :email
9
- # as the login.
10
-
11
- :password_attribute_name, # change *virtual* password attribute, the one which is used
12
- # until an encrypted one is generated.
13
-
14
- :email_attribute_name, # change default email attribute.
15
-
16
- :downcase_username_before_authenticating, # downcase the username before trying to authenticate, default is false
17
-
18
- :crypted_password_attribute_name, # change default crypted_password attribute.
19
- :salt_join_token, # what pattern to use to join the password with the salt
20
- :salt_attribute_name, # change default salt attribute.
21
- :stretches, # how many times to apply encryption to the password.
22
- :encryption_key, # encryption key used to encrypt reversible encryptions such as
23
- # AES256.
24
-
25
- :subclasses_inherit_config, # make this configuration inheritable for subclasses. Useful for
26
- # ActiveRecord's STI.
27
-
28
- :submodules, # configured in config/application.rb
29
- :before_authenticate, # an array of method names to call before authentication
30
- # completes. used internally.
31
-
32
- :after_config # an array of method names to call after configuration by user.
33
- # used internally.
34
-
35
- attr_reader :encryption_provider, # change default encryption_provider.
36
- :custom_encryption_provider, # use an external encryption class.
37
- :encryption_algorithm # encryption algorithm name. See 'encryption_algorithm=' below
38
- # for available options.
7
+ # change *virtual* password attribute, the one which is used until an encrypted one is generated.
8
+ attr_accessor :password_attribute_name
9
+ # change default email attribute.
10
+ attr_accessor :email_attribute_name
11
+ # downcase the username before trying to authenticate, default is false
12
+ attr_accessor :downcase_username_before_authenticating
13
+ # change default crypted_password attribute.
14
+ attr_accessor :crypted_password_attribute_name
15
+ # application-specific secret token that is joined with the password and its salt.
16
+ # Currently available with BCrypt (default crypt provider) only.
17
+ attr_accessor :pepper
18
+ # what pattern to use to join the password with the salt
19
+ # APPLICABLE TO MD5, SHA1, SHA256, SHA512. Other crypt providers (incl. BCrypt) ignore this parameter.
20
+ attr_accessor :salt_join_token
21
+ # change default salt attribute.
22
+ attr_accessor :salt_attribute_name
23
+ # how many times to apply encryption to the password.
24
+ attr_accessor :stretches
25
+ # encryption key used to encrypt reversible encryptions such as AES256.
26
+ attr_accessor :encryption_key
27
+ # make this configuration inheritable for subclasses. Useful for ActiveRecord's STI.
28
+ attr_accessor :subclasses_inherit_config
29
+ # configured in config/application.rb
30
+ attr_accessor :submodules
31
+ # an array of method names to call before authentication completes. used internally.
32
+ attr_accessor :before_authenticate
33
+ # method to send email related
34
+ # options: `:deliver_later`, `:deliver_now`, `:deliver`
35
+ # Default: :deliver (Rails version < 4.2) or :deliver_now (Rails version 4.2+)
36
+ # method to send email related
37
+ attr_accessor :email_delivery_method
38
+ # an array of method names to call after configuration by user. used internally.
39
+ attr_accessor :after_config
40
+ # Set token randomness
41
+ attr_accessor :token_randomness
42
+
43
+ # change default username attribute, for example, to use :email as the login. See 'username_attribute_names=' below.
44
+ attr_reader :username_attribute_names
45
+ # change default encryption_provider.
46
+ attr_reader :encryption_provider
47
+ # use an external encryption class.
48
+ attr_reader :custom_encryption_provider
49
+ # encryption algorithm name. See 'encryption_algorithm=' below for available options.
50
+ attr_reader :encryption_algorithm
39
51
 
40
52
  def initialize
41
53
  @defaults = {
42
54
  :@submodules => [],
43
- :@username_attribute_names => [:email],
55
+ :@username_attribute_names => [:email],
44
56
  :@password_attribute_name => :password,
45
57
  :@downcase_username_before_authenticating => false,
46
58
  :@email_attribute_name => :email,
@@ -49,25 +61,28 @@ module Sorcery
49
61
  :@encryption_provider => CryptoProviders::BCrypt,
50
62
  :@custom_encryption_provider => nil,
51
63
  :@encryption_key => nil,
52
- :@salt_join_token => "",
64
+ :@pepper => '',
65
+ :@salt_join_token => '',
53
66
  :@salt_attribute_name => :salt,
54
67
  :@stretches => nil,
55
68
  :@subclasses_inherit_config => false,
56
69
  :@before_authenticate => [],
57
- :@after_config => []
70
+ :@after_config => [],
71
+ :@email_delivery_method => default_email_delivery_method,
72
+ :@token_randomness => 15
58
73
  }
59
74
  reset!
60
75
  end
61
76
 
62
77
  # Resets all configuration options to their default values.
63
78
  def reset!
64
- @defaults.each do |k,v|
65
- instance_variable_set(k,v)
79
+ @defaults.each do |k, v|
80
+ instance_variable_set(k, v)
66
81
  end
67
82
  end
68
83
 
69
84
  def username_attribute_names=(fields)
70
- @username_attribute_names = fields.kind_of?(Array) ? fields : [fields]
85
+ @username_attribute_names = fields.is_a?(Array) ? fields : [fields]
71
86
  end
72
87
 
73
88
  def custom_encryption_provider=(provider)
@@ -77,20 +92,28 @@ module Sorcery
77
92
  def encryption_algorithm=(algo)
78
93
  @encryption_algorithm = algo
79
94
  @encryption_provider = case @encryption_algorithm.to_sym
80
- when :none then nil
81
- when :md5 then CryptoProviders::MD5
82
- when :sha1 then CryptoProviders::SHA1
83
- when :sha256 then CryptoProviders::SHA256
84
- when :sha512 then CryptoProviders::SHA512
85
- when :aes256 then CryptoProviders::AES256
86
- when :bcrypt then CryptoProviders::BCrypt
87
- when :custom then @custom_encryption_provider
88
- else raise ArgumentError.new("Encryption algorithm supplied, #{algo}, is invalid")
89
- end
95
+ when :none then nil
96
+ when :md5 then CryptoProviders::MD5
97
+ when :sha1 then CryptoProviders::SHA1
98
+ when :sha256 then CryptoProviders::SHA256
99
+ when :sha512 then CryptoProviders::SHA512
100
+ when :aes256 then CryptoProviders::AES256
101
+ when :bcrypt then CryptoProviders::BCrypt
102
+ when :custom then @custom_encryption_provider
103
+ else raise ArgumentError, "Encryption algorithm supplied, #{algo}, is invalid"
104
+ end
90
105
  end
91
106
 
92
- end
107
+ private
108
+
109
+ def default_email_delivery_method
110
+ # Rails 4.2 deprecates #deliver
111
+ rails_version_bigger_than_or_equal?('4.2.0') ? :deliver_now : :deliver
112
+ end
93
113
 
114
+ def rails_version_bigger_than_or_equal?(version)
115
+ Gem::Version.new(version) <= Gem::Version.new(Rails.version)
116
+ end
117
+ end
94
118
  end
95
119
  end
96
-
@@ -12,12 +12,16 @@ module Sorcery
12
12
  base.send(:include, InstanceMethods)
13
13
 
14
14
  base.sorcery_config.class_eval do
15
- attr_accessor :last_login_at_attribute_name, # last login attribute name.
16
- :last_logout_at_attribute_name, # last logout attribute name.
17
- :last_activity_at_attribute_name, # last activity attribute name.
18
- :last_login_from_ip_address_name, # last activity login source
19
- :activity_timeout # how long since last activity is
20
- #the user defined logged out?
15
+ # last login attribute name.
16
+ attr_accessor :last_login_at_attribute_name
17
+ # last logout attribute name.
18
+ attr_accessor :last_logout_at_attribute_name
19
+ # last activity attribute name.
20
+ attr_accessor :last_activity_at_attribute_name
21
+ # last activity login source
22
+ attr_accessor :last_login_from_ip_address_name
23
+ # how long since last activity is the user defined offline
24
+ attr_accessor :activity_timeout
21
25
  end
22
26
 
23
27
  base.sorcery_config.instance_eval do
@@ -45,18 +49,33 @@ module Sorcery
45
49
  sorcery_adapter.update_attribute(sorcery_config.last_activity_at_attribute_name, time)
46
50
  end
47
51
 
48
- def set_last_ip_addess(ip_address)
52
+ def set_last_ip_address(ip_address)
49
53
  sorcery_adapter.update_attribute(sorcery_config.last_login_from_ip_address_name, ip_address)
50
54
  end
51
- end
52
55
 
53
- module ClassMethods
54
- # get all users with last_activity within timeout
55
- def current_users
56
- sorcery_adapter.get_current_users
56
+ # online method shows if user is active (logout action makes user inactive too)
57
+ def online?
58
+ return false if send(sorcery_config.last_activity_at_attribute_name).nil?
59
+
60
+ logged_in? && send(sorcery_config.last_activity_at_attribute_name) > sorcery_config.activity_timeout.seconds.ago
61
+ end
62
+
63
+ # shows if user is logged in, but it not show if user is online - see online?
64
+ def logged_in?
65
+ return false if send(sorcery_config.last_login_at_attribute_name).nil?
66
+ return true if send(sorcery_config.last_login_at_attribute_name).present? && send(sorcery_config.last_logout_at_attribute_name).nil?
67
+
68
+ send(sorcery_config.last_login_at_attribute_name) > send(sorcery_config.last_logout_at_attribute_name)
57
69
  end
58
70
 
71
+ def logged_out?
72
+ !logged_in?
73
+ end
74
+ end
75
+
76
+ module ClassMethods
59
77
  protected
78
+
60
79
  def define_activity_logging_fields
61
80
  sorcery_adapter.define_field sorcery_config.last_login_at_attribute_name, Time
62
81
  sorcery_adapter.define_field sorcery_config.last_logout_at_attribute_name, Time
@@ -10,15 +10,14 @@ module Sorcery
10
10
  base.sorcery_config.class_eval do
11
11
  attr_accessor :failed_logins_count_attribute_name, # failed logins attribute name.
12
12
  :lock_expires_at_attribute_name, # this field indicates whether user
13
- # is banned and when it will be active again.
13
+ # is banned and when it will be active again.
14
14
  :consecutive_login_retries_amount_limit, # how many failed logins allowed.
15
15
  :login_lock_time_period, # how long the user should be banned.
16
- # in seconds. 0 for permanent.
17
-
16
+ # in seconds. 0 for permanent.
18
17
  :unlock_token_attribute_name, # Unlock token attribute name
19
18
  :unlock_token_email_method_name, # Mailer method name
20
19
  :unlock_token_mailer_disabled, # When true, dont send unlock token via email
21
- :unlock_token_mailer # Mailer class
20
+ :unlock_token_mailer # Mailer class
22
21
  end
23
22
 
24
23
  base.sorcery_config.instance_eval do
@@ -41,16 +40,21 @@ module Sorcery
41
40
  end
42
41
 
43
42
  module ClassMethods
44
- def load_from_unlock_token(token)
45
- return nil if token.blank?
46
- user = sorcery_adapter.find_by_token(sorcery_config.unlock_token_attribute_name,token)
47
- user
43
+ # This doesn't check to see if the account is still locked
44
+ def load_from_unlock_token(token, &block)
45
+ return if token.blank?
46
+
47
+ load_from_token(
48
+ token,
49
+ sorcery_config.unlock_token_attribute_name,
50
+ &block
51
+ )
48
52
  end
49
53
 
50
54
  protected
51
55
 
52
56
  def define_brute_force_protection_fields
53
- sorcery_adapter.define_field sorcery_config.failed_logins_count_attribute_name, Integer, :default => 0
57
+ sorcery_adapter.define_field sorcery_config.failed_logins_count_attribute_name, Integer, default: 0
54
58
  sorcery_adapter.define_field sorcery_config.lock_expires_at_attribute_name, Time
55
59
  sorcery_adapter.define_field sorcery_config.unlock_token_attribute_name, String
56
60
  end
@@ -58,49 +62,49 @@ module Sorcery
58
62
 
59
63
  module InstanceMethods
60
64
  # Called by the controller to increment the failed logins counter.
61
- # Calls 'lock!' if login retries limit was reached.
65
+ # Calls 'login_lock!' if login retries limit was reached.
62
66
  def register_failed_login!
63
67
  config = sorcery_config
64
- return if !unlocked?
68
+ return unless login_unlocked?
65
69
 
66
70
  sorcery_adapter.increment(config.failed_logins_count_attribute_name)
67
71
 
68
- if self.send(config.failed_logins_count_attribute_name) >= config.consecutive_login_retries_amount_limit
69
- lock!
70
- end
72
+ return unless send(config.failed_logins_count_attribute_name) >= config.consecutive_login_retries_amount_limit
73
+
74
+ login_lock!
71
75
  end
72
76
 
73
77
  # /!\
74
78
  # Moved out of protected for use like activate! in controller
75
79
  # /!\
76
- def unlock!
80
+ def login_unlock!
77
81
  config = sorcery_config
78
- attributes = {config.lock_expires_at_attribute_name => nil,
79
- config.failed_logins_count_attribute_name => 0,
80
- config.unlock_token_attribute_name => nil}
82
+ attributes = { config.lock_expires_at_attribute_name => nil,
83
+ config.failed_logins_count_attribute_name => 0,
84
+ config.unlock_token_attribute_name => nil }
81
85
  sorcery_adapter.update_attributes(attributes)
82
86
  end
83
87
 
84
- def locked?
85
- !unlocked?
88
+ def login_locked?
89
+ !login_unlocked?
86
90
  end
87
91
 
88
92
  protected
89
93
 
90
- def lock!
94
+ def login_lock!
91
95
  config = sorcery_config
92
- attributes = {config.lock_expires_at_attribute_name => Time.now.in_time_zone + config.login_lock_time_period,
93
- config.unlock_token_attribute_name => TemporaryToken.generate_random_token}
96
+ attributes = { config.lock_expires_at_attribute_name => Time.now.in_time_zone + config.login_lock_time_period,
97
+ config.unlock_token_attribute_name => TemporaryToken.generate_random_token }
94
98
  sorcery_adapter.update_attributes(attributes)
95
99
 
96
- unless config.unlock_token_mailer_disabled || config.unlock_token_mailer.nil?
97
- send_unlock_token_email!
98
- end
100
+ return if config.unlock_token_mailer_disabled || config.unlock_token_mailer.nil?
101
+
102
+ send_unlock_token_email!
99
103
  end
100
104
 
101
- def unlocked?
105
+ def login_unlocked?
102
106
  config = sorcery_config
103
- self.send(config.lock_expires_at_attribute_name).nil?
107
+ send(config.lock_expires_at_attribute_name).nil?
104
108
  end
105
109
 
106
110
  def send_unlock_token_email!
@@ -113,10 +117,13 @@ module Sorcery
113
117
  # Runs as a hook before authenticate.
114
118
  def prevent_locked_user_login
115
119
  config = sorcery_config
116
- if !self.unlocked? && config.login_lock_time_period != 0
117
- self.unlock! if self.send(config.lock_expires_at_attribute_name) <= Time.now.in_time_zone
120
+ if !login_unlocked? && config.login_lock_time_period != 0
121
+ login_unlock! if send(config.lock_expires_at_attribute_name) <= Time.now.in_time_zone
118
122
  end
119
- unlocked?
123
+
124
+ return false, :locked unless login_unlocked?
125
+
126
+ true
120
127
  end
121
128
  end
122
129
  end
@@ -20,7 +20,6 @@ module Sorcery
20
20
  :authentications_user_id_attribute_name,
21
21
  :provider_attribute_name,
22
22
  :provider_uid_attribute_name
23
-
24
23
  end
25
24
 
26
25
  base.sorcery_config.instance_eval do
@@ -34,20 +33,20 @@ module Sorcery
34
33
 
35
34
  base.send(:include, InstanceMethods)
36
35
  base.extend(ClassMethods)
37
-
38
36
  end
39
37
 
40
38
  module ClassMethods
41
39
  # takes a provider and uid and finds a user by them.
42
- def load_from_provider(provider,uid)
40
+ def load_from_provider(provider, uid)
43
41
  config = sorcery_config
44
42
  authentication = config.authentications_class.sorcery_adapter.find_by_oauth_credentials(provider, uid)
45
- user = sorcery_adapter.find_by_id(authentication.send(config.authentications_user_id_attribute_name)) if authentication
43
+ # Return user if matching authentication found
44
+ sorcery_adapter.find_by_id(authentication.send(config.authentications_user_id_attribute_name)) if authentication
46
45
  end
47
46
 
48
47
  def create_and_validate_from_provider(provider, uid, attrs)
49
48
  user = new(attrs)
50
- user.send(sorcery_config.authentications_class.to_s.downcase.pluralize).build(
49
+ user.send(sorcery_config.authentications_class.name.demodulize.underscore.pluralize).build(
51
50
  sorcery_config.provider_uid_attribute_name => uid,
52
51
  sorcery_config.provider_attribute_name => provider
53
52
  )
@@ -57,7 +56,7 @@ module Sorcery
57
56
 
58
57
  def create_from_provider(provider, uid, attrs)
59
58
  user = new
60
- attrs.each do |k,v|
59
+ attrs.each do |k, v|
61
60
  user.send(:"#{k}=", v)
62
61
  end
63
62
 
@@ -66,7 +65,7 @@ module Sorcery
66
65
  end
67
66
 
68
67
  sorcery_adapter.transaction do
69
- user.sorcery_adapter.save(:validate => false)
68
+ user.sorcery_adapter.save(validate: false)
70
69
  sorcery_config.authentications_class.create!(
71
70
  sorcery_config.authentications_user_id_attribute_name => user.id,
72
71
  sorcery_config.provider_attribute_name => provider,
@@ -75,11 +74,26 @@ module Sorcery
75
74
  end
76
75
  user
77
76
  end
77
+
78
+ # NOTE: Should this build the authentication as well and return [user, auth]?
79
+ # Currently, users call this function for the user and call add_provider_to_user after saving
80
+ def build_from_provider(attrs)
81
+ user = new
82
+ attrs.each do |k, v|
83
+ user.send(:"#{k}=", v)
84
+ end
85
+
86
+ if block_given?
87
+ return false unless yield user
88
+ end
89
+
90
+ user
91
+ end
78
92
  end
79
93
 
80
94
  module InstanceMethods
81
95
  def add_provider_to_user(provider, uid)
82
- authentications = sorcery_config.authentications_class.name.underscore.pluralize
96
+ authentications = sorcery_config.authentications_class.name.demodulize.underscore.pluralize
83
97
  # first check to see if user has a particular authentication already
84
98
  if sorcery_adapter.find_authentication_by_oauth_credentials(authentications, provider, uid).nil?
85
99
  user = send(authentications).build(sorcery_config.provider_uid_attribute_name => uid,
@@ -91,9 +105,7 @@ module Sorcery
91
105
 
92
106
  user
93
107
  end
94
-
95
108
  end
96
-
97
109
  end
98
110
  end
99
111
  end
@@ -0,0 +1,130 @@
1
+ module Sorcery
2
+ module Model
3
+ module Submodules
4
+ # This submodule adds the ability to login via email without password.
5
+ # When the user requests an email is sent to him with a url.
6
+ # The url includes a token, which is also saved with the user's record in the db.
7
+ # The token has configurable expiration.
8
+ # When the user clicks the url in the email, providing the token has not yet expired,
9
+ # he will be able to login.
10
+ #
11
+ # When using this submodule, supplying a mailer is mandatory.
12
+ module MagicLogin
13
+ def self.included(base)
14
+ base.sorcery_config.class_eval do
15
+ attr_accessor :magic_login_token_attribute_name, # magic login code attribute name.
16
+ :magic_login_token_expires_at_attribute_name, # expires at attribute name.
17
+ :magic_login_email_sent_at_attribute_name, # when was email sent, used for hammering
18
+ # protection.
19
+ :magic_login_mailer_class, # mailer class. Needed.
20
+ :magic_login_mailer_disabled, # when true sorcery will not automatically
21
+ # email magic login details and allow you to
22
+ # manually handle how and when email is sent
23
+ :magic_login_email_method_name, # magic login email method on your
24
+ # mailer class.
25
+ :magic_login_expiration_period, # how many seconds before the request
26
+ # expires. nil for never expires.
27
+ :magic_login_time_between_emails # hammering protection, how long to wait
28
+ # before allowing another email to be sent.
29
+ end
30
+
31
+ base.sorcery_config.instance_eval do
32
+ @defaults.merge!(:@magic_login_token_attribute_name => :magic_login_token,
33
+ :@magic_login_token_expires_at_attribute_name => :magic_login_token_expires_at,
34
+ :@magic_login_email_sent_at_attribute_name => :magic_login_email_sent_at,
35
+ :@magic_login_mailer_class => nil,
36
+ :@magic_login_mailer_disabled => true,
37
+ :@magic_login_email_method_name => :magic_login_email,
38
+ :@magic_login_expiration_period => 15 * 60,
39
+ :@magic_login_time_between_emails => 5 * 60)
40
+
41
+ reset!
42
+ end
43
+
44
+ base.extend(ClassMethods)
45
+
46
+ base.sorcery_config.after_config << :validate_mailer_defined
47
+ base.sorcery_config.after_config << :define_magic_login_fields
48
+
49
+ base.send(:include, InstanceMethods)
50
+ end
51
+
52
+ module ClassMethods
53
+ # Find user by token, also checks for expiration.
54
+ # Returns the user if token found and is valid.
55
+ def load_from_magic_login_token(token, &block)
56
+ load_from_token(
57
+ token,
58
+ @sorcery_config.magic_login_token_attribute_name,
59
+ @sorcery_config.magic_login_token_expires_at_attribute_name,
60
+ &block
61
+ )
62
+ end
63
+
64
+ protected
65
+
66
+ # This submodule requires the developer to define his own mailer class to be used by it
67
+ # when magic_login_mailer_disabled is false
68
+ def validate_mailer_defined
69
+ msg = 'To use magic_login submodule, you must define a mailer (config.magic_login_mailer_class = YourMailerClass).'
70
+ raise ArgumentError, msg if @sorcery_config.magic_login_mailer_class.nil? && @sorcery_config.magic_login_mailer_disabled == false
71
+ end
72
+
73
+ def define_magic_login_fields
74
+ sorcery_adapter.define_field sorcery_config.magic_login_token_attribute_name, String
75
+ sorcery_adapter.define_field sorcery_config.magic_login_token_expires_at_attribute_name, Time
76
+ sorcery_adapter.define_field sorcery_config.magic_login_email_sent_at_attribute_name, Time
77
+ end
78
+ end
79
+
80
+ module InstanceMethods
81
+ # generates a reset code with expiration
82
+ def generate_magic_login_token!
83
+ config = sorcery_config
84
+ attributes = {
85
+ config.magic_login_token_attribute_name => TemporaryToken.generate_random_token,
86
+ config.magic_login_email_sent_at_attribute_name => Time.now.in_time_zone
87
+ }
88
+ attributes[config.magic_login_token_expires_at_attribute_name] = Time.now.in_time_zone + config.magic_login_expiration_period if config.magic_login_expiration_period
89
+
90
+ sorcery_adapter.update_attributes(attributes)
91
+ end
92
+
93
+ # generates a magic login code with expiration and sends an email to the user.
94
+ def deliver_magic_login_instructions!
95
+ mail = false
96
+ config = sorcery_config
97
+ # hammering protection
98
+ return false if !config.magic_login_time_between_emails.nil? &&
99
+ send(config.magic_login_email_sent_at_attribute_name) &&
100
+ send(config.magic_login_email_sent_at_attribute_name) > config.magic_login_time_between_emails.seconds.ago
101
+
102
+ self.class.sorcery_adapter.transaction do
103
+ generate_magic_login_token!
104
+ unless config.magic_login_mailer_disabled
105
+ send_magic_login_email!
106
+ mail = true
107
+ end
108
+ end
109
+ mail
110
+ end
111
+
112
+ # Clears the token.
113
+ def clear_magic_login_token!
114
+ config = sorcery_config
115
+ sorcery_adapter.update_attributes(
116
+ config.magic_login_token_attribute_name => nil,
117
+ config.magic_login_token_expires_at_attribute_name => nil
118
+ )
119
+ end
120
+
121
+ protected
122
+
123
+ def send_magic_login_email!
124
+ generic_send_email(:magic_login_email_method_name, :magic_login_mailer_class)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end