authlogic 4.5.0 → 6.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/lib/authlogic/acts_as_authentic/base.rb +19 -19
  3. data/lib/authlogic/acts_as_authentic/email.rb +3 -170
  4. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +3 -1
  5. data/lib/authlogic/acts_as_authentic/login.rb +7 -174
  6. data/lib/authlogic/acts_as_authentic/magic_columns.rb +7 -4
  7. data/lib/authlogic/acts_as_authentic/password.rb +67 -256
  8. data/lib/authlogic/acts_as_authentic/perishable_token.rb +8 -5
  9. data/lib/authlogic/acts_as_authentic/persistence_token.rb +10 -4
  10. data/lib/authlogic/acts_as_authentic/queries/case_sensitivity.rb +53 -0
  11. data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +36 -20
  12. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +12 -8
  13. data/lib/authlogic/acts_as_authentic/single_access_token.rb +10 -8
  14. data/lib/authlogic/config.rb +9 -1
  15. data/lib/authlogic/controller_adapters/abstract_adapter.rb +28 -4
  16. data/lib/authlogic/controller_adapters/rack_adapter.rb +2 -0
  17. data/lib/authlogic/controller_adapters/rails_adapter.rb +7 -30
  18. data/lib/authlogic/controller_adapters/sinatra_adapter.rb +6 -0
  19. data/lib/authlogic/cookie_credentials.rb +63 -0
  20. data/lib/authlogic/crypto_providers/bcrypt.rb +3 -3
  21. data/lib/authlogic/crypto_providers/md5/v2.rb +35 -0
  22. data/lib/authlogic/crypto_providers/md5.rb +6 -6
  23. data/lib/authlogic/crypto_providers/scrypt.rb +2 -0
  24. data/lib/authlogic/crypto_providers/sha1/v2.rb +41 -0
  25. data/lib/authlogic/crypto_providers/sha1.rb +7 -6
  26. data/lib/authlogic/crypto_providers/sha256/v2.rb +58 -0
  27. data/lib/authlogic/crypto_providers/sha256.rb +5 -0
  28. data/lib/authlogic/crypto_providers/sha512/v2.rb +39 -0
  29. data/lib/authlogic/crypto_providers/sha512.rb +9 -5
  30. data/lib/authlogic/crypto_providers.rb +5 -20
  31. data/lib/authlogic/errors.rb +50 -0
  32. data/lib/authlogic/i18n/translator.rb +4 -1
  33. data/lib/authlogic/i18n.rb +3 -1
  34. data/lib/authlogic/random.rb +2 -0
  35. data/lib/authlogic/session/base.rb +2197 -39
  36. data/lib/authlogic/session/magic_column/assigns_last_request_at.rb +46 -0
  37. data/lib/authlogic/test_case/mock_api_controller.rb +52 -0
  38. data/lib/authlogic/test_case/mock_controller.rb +3 -1
  39. data/lib/authlogic/test_case/mock_cookie_jar.rb +32 -6
  40. data/lib/authlogic/test_case/mock_logger.rb +2 -0
  41. data/lib/authlogic/test_case/mock_request.rb +12 -0
  42. data/lib/authlogic/test_case/rails_request_adapter.rb +9 -1
  43. data/lib/authlogic/test_case.rb +5 -0
  44. data/lib/authlogic/version.rb +2 -1
  45. data/lib/authlogic.rb +5 -28
  46. metadata +175 -200
  47. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -28
  48. data/.github/ISSUE_TEMPLATE/feature_proposal.md +0 -32
  49. data/.github/triage.md +0 -86
  50. data/.gitignore +0 -15
  51. data/.rubocop.yml +0 -133
  52. data/.rubocop_todo.yml +0 -74
  53. data/.travis.yml +0 -24
  54. data/CHANGELOG.md +0 -348
  55. data/CONTRIBUTING.md +0 -91
  56. data/Gemfile +0 -6
  57. data/LICENSE +0 -20
  58. data/README.md +0 -448
  59. data/Rakefile +0 -21
  60. data/UPGRADING.md +0 -22
  61. data/authlogic.gemspec +0 -40
  62. data/doc/use_normal_rails_validation.md +0 -82
  63. data/gemfiles/Gemfile.rails-4.2.x +0 -6
  64. data/gemfiles/Gemfile.rails-5.1.x +0 -6
  65. data/gemfiles/Gemfile.rails-5.2.x +0 -6
  66. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +0 -106
  67. data/lib/authlogic/acts_as_authentic/validations_scope.rb +0 -35
  68. data/lib/authlogic/authenticates_many/association.rb +0 -50
  69. data/lib/authlogic/authenticates_many/base.rb +0 -81
  70. data/lib/authlogic/crypto_providers/aes256.rb +0 -71
  71. data/lib/authlogic/crypto_providers/wordpress.rb +0 -72
  72. data/lib/authlogic/regex.rb +0 -79
  73. data/lib/authlogic/session/activation.rb +0 -73
  74. data/lib/authlogic/session/active_record_trickery.rb +0 -65
  75. data/lib/authlogic/session/brute_force_protection.rb +0 -127
  76. data/lib/authlogic/session/callbacks.rb +0 -153
  77. data/lib/authlogic/session/cookies.rb +0 -329
  78. data/lib/authlogic/session/existence.rb +0 -103
  79. data/lib/authlogic/session/foundation.rb +0 -105
  80. data/lib/authlogic/session/http_auth.rb +0 -107
  81. data/lib/authlogic/session/id.rb +0 -53
  82. data/lib/authlogic/session/klass.rb +0 -73
  83. data/lib/authlogic/session/magic_columns.rb +0 -119
  84. data/lib/authlogic/session/magic_states.rb +0 -82
  85. data/lib/authlogic/session/params.rb +0 -130
  86. data/lib/authlogic/session/password.rb +0 -318
  87. data/lib/authlogic/session/perishable_token.rb +0 -24
  88. data/lib/authlogic/session/persistence.rb +0 -77
  89. data/lib/authlogic/session/priority_record.rb +0 -38
  90. data/lib/authlogic/session/scopes.rb +0 -138
  91. data/lib/authlogic/session/session.rb +0 -77
  92. data/lib/authlogic/session/timeout.rb +0 -103
  93. data/lib/authlogic/session/unauthorized_record.rb +0 -56
  94. data/lib/authlogic/session/validation.rb +0 -93
  95. data/test/acts_as_authentic_test/base_test.rb +0 -27
  96. data/test/acts_as_authentic_test/email_test.rb +0 -241
  97. data/test/acts_as_authentic_test/logged_in_status_test.rb +0 -64
  98. data/test/acts_as_authentic_test/login_test.rb +0 -153
  99. data/test/acts_as_authentic_test/magic_columns_test.rb +0 -29
  100. data/test/acts_as_authentic_test/password_test.rb +0 -263
  101. data/test/acts_as_authentic_test/perishable_token_test.rb +0 -98
  102. data/test/acts_as_authentic_test/persistence_token_test.rb +0 -62
  103. data/test/acts_as_authentic_test/restful_authentication_test.rb +0 -48
  104. data/test/acts_as_authentic_test/session_maintenance_test.rb +0 -150
  105. data/test/acts_as_authentic_test/single_access_test.rb +0 -46
  106. data/test/adapter_test.rb +0 -23
  107. data/test/authenticates_many_test.rb +0 -33
  108. data/test/config_test.rb +0 -38
  109. data/test/crypto_provider_test/aes256_test.rb +0 -16
  110. data/test/crypto_provider_test/bcrypt_test.rb +0 -16
  111. data/test/crypto_provider_test/scrypt_test.rb +0 -16
  112. data/test/crypto_provider_test/sha1_test.rb +0 -25
  113. data/test/crypto_provider_test/sha256_test.rb +0 -16
  114. data/test/crypto_provider_test/sha512_test.rb +0 -16
  115. data/test/crypto_provider_test/wordpress_test.rb +0 -26
  116. data/test/fixtures/companies.yml +0 -5
  117. data/test/fixtures/employees.yml +0 -17
  118. data/test/fixtures/projects.yml +0 -3
  119. data/test/fixtures/users.yml +0 -41
  120. data/test/i18n/lol.yml +0 -4
  121. data/test/i18n_test.rb +0 -35
  122. data/test/libs/affiliate.rb +0 -9
  123. data/test/libs/company.rb +0 -8
  124. data/test/libs/employee.rb +0 -9
  125. data/test/libs/employee_session.rb +0 -4
  126. data/test/libs/ldaper.rb +0 -5
  127. data/test/libs/project.rb +0 -5
  128. data/test/libs/user.rb +0 -9
  129. data/test/libs/user_session.rb +0 -27
  130. data/test/random_test.rb +0 -15
  131. data/test/session_test/activation_test.rb +0 -45
  132. data/test/session_test/active_record_trickery_test.rb +0 -78
  133. data/test/session_test/brute_force_protection_test.rb +0 -110
  134. data/test/session_test/callbacks_test.rb +0 -42
  135. data/test/session_test/cookies_test.rb +0 -244
  136. data/test/session_test/credentials_test.rb +0 -0
  137. data/test/session_test/existence_test.rb +0 -88
  138. data/test/session_test/foundation_test.rb +0 -24
  139. data/test/session_test/http_auth_test.rb +0 -60
  140. data/test/session_test/id_test.rb +0 -19
  141. data/test/session_test/klass_test.rb +0 -42
  142. data/test/session_test/magic_columns_test.rb +0 -62
  143. data/test/session_test/magic_states_test.rb +0 -60
  144. data/test/session_test/params_test.rb +0 -61
  145. data/test/session_test/password_test.rb +0 -107
  146. data/test/session_test/perishability_test.rb +0 -17
  147. data/test/session_test/persistence_test.rb +0 -35
  148. data/test/session_test/scopes_test.rb +0 -68
  149. data/test/session_test/session_test.rb +0 -80
  150. data/test/session_test/timeout_test.rb +0 -84
  151. data/test/session_test/unauthorized_record_test.rb +0 -15
  152. data/test/session_test/validation_test.rb +0 -25
  153. data/test/test_helper.rb +0 -272
@@ -1,127 +0,0 @@
1
- module Authlogic
2
- module Session
3
- # A brute force attacks is executed by hammering a login with as many password
4
- # combinations as possible, until one works. A brute force attacked is generally
5
- # combated with a slow hashing algorithm such as BCrypt. You can increase the cost,
6
- # which makes the hash generation slower, and ultimately increases the time it takes
7
- # to execute a brute force attack. Just to put this into perspective, if a hacker was
8
- # to gain access to your server and execute a brute force attack locally, meaning
9
- # there is no network lag, it would probably take decades to complete. Now throw in
10
- # network lag and it would take MUCH longer.
11
- #
12
- # But for those that are extra paranoid and can't get enough protection, why not stop
13
- # them as soon as you realize something isn't right? That's what this module is all
14
- # about. By default the consecutive_failed_logins_limit configuration option is set to
15
- # 50, if someone consecutively fails to login after 50 attempts their account will be
16
- # suspended. This is a very liberal number and at this point it should be obvious that
17
- # something is not right. If you wish to lower this number just set the configuration
18
- # to a lower number:
19
- #
20
- # class UserSession < Authlogic::Session::Base
21
- # consecutive_failed_logins_limit 10
22
- # end
23
- module BruteForceProtection
24
- def self.included(klass)
25
- klass.class_eval do
26
- extend Config
27
- include InstanceMethods
28
- validate :reset_failed_login_count, if: :reset_failed_login_count?
29
- validate :validate_failed_logins, if: :being_brute_force_protected?
30
- end
31
- end
32
-
33
- # Configuration for the brute force protection feature.
34
- module Config
35
- # To help protect from brute force attacks you can set a limit on the
36
- # allowed number of consecutive failed logins. By default this is 50,
37
- # this is a very liberal number, and if someone fails to login after 50
38
- # tries it should be pretty obvious that it's a machine trying to login
39
- # in and very likely a brute force attack.
40
- #
41
- # In order to enable this field your model MUST have a
42
- # failed_login_count (integer) field.
43
- #
44
- # If you don't know what a brute force attack is, it's when a machine
45
- # tries to login into a system using every combination of character
46
- # possible. Thus resulting in possibly millions of attempts to log into
47
- # an account.
48
- #
49
- # * <tt>Default:</tt> 50
50
- # * <tt>Accepts:</tt> Integer, set to 0 to disable
51
- def consecutive_failed_logins_limit(value = nil)
52
- rw_config(:consecutive_failed_logins_limit, value, 50)
53
- end
54
- alias_method :consecutive_failed_logins_limit=, :consecutive_failed_logins_limit
55
-
56
- # Once the failed logins limit has been exceed, how long do you want to
57
- # ban the user? This can be a temporary or permanent ban.
58
- #
59
- # * <tt>Default:</tt> 2.hours
60
- # * <tt>Accepts:</tt> Fixnum, set to 0 for permanent ban
61
- def failed_login_ban_for(value = nil)
62
- rw_config(:failed_login_ban_for, (!value.nil? && value) || value, 2.hours.to_i)
63
- end
64
- alias_method :failed_login_ban_for=, :failed_login_ban_for
65
- end
66
-
67
- # The methods available for an Authlogic::Session::Base object that make
68
- # up the brute force protection feature.
69
- module InstanceMethods
70
- # Returns true when the consecutive_failed_logins_limit has been
71
- # exceeded and is being temporarily banned. Notice the word temporary,
72
- # the user will not be permanently banned unless you choose to do so
73
- # with configuration. By default they will be banned for 2 hours. During
74
- # that 2 hour period this method will return true.
75
- def being_brute_force_protected?
76
- exceeded_failed_logins_limit? &&
77
- (
78
- failed_login_ban_for <= 0 ||
79
- attempted_record.respond_to?(:updated_at) &&
80
- attempted_record.updated_at >= failed_login_ban_for.seconds.ago
81
- )
82
- end
83
-
84
- private
85
-
86
- def exceeded_failed_logins_limit?
87
- !attempted_record.nil? &&
88
- attempted_record.respond_to?(:failed_login_count) &&
89
- consecutive_failed_logins_limit > 0 &&
90
- attempted_record.failed_login_count &&
91
- attempted_record.failed_login_count >= consecutive_failed_logins_limit
92
- end
93
-
94
- def reset_failed_login_count?
95
- exceeded_failed_logins_limit? && !being_brute_force_protected?
96
- end
97
-
98
- def reset_failed_login_count
99
- attempted_record.failed_login_count = 0
100
- end
101
-
102
- def validate_failed_logins
103
- # Clear all other error messages, as they are irrelevant at this point and can
104
- # only provide additional information that is not needed
105
- errors.clear
106
- errors.add(
107
- :base,
108
- I18n.t(
109
- "error_messages.consecutive_failed_logins_limit_exceeded",
110
- default: "Consecutive failed logins limit exceeded, account has been" +
111
- (failed_login_ban_for.zero? ? "" : " temporarily") +
112
- " disabled."
113
- )
114
- )
115
- end
116
-
117
- def consecutive_failed_logins_limit
118
- self.class.consecutive_failed_logins_limit
119
- end
120
-
121
- def failed_login_ban_for
122
- self.class.failed_login_ban_for
123
- end
124
- end
125
- end
126
- end
127
- end
@@ -1,153 +0,0 @@
1
- module Authlogic
2
- module Session
3
- # Between these callbacks and the configuration, this is the contract between me and
4
- # you to safely modify Authlogic's behavior. I will do everything I can to make sure
5
- # these do not change.
6
- #
7
- # Check out the sub modules of Authlogic::Session. They are very concise, clear, and
8
- # to the point. More importantly they use the same API that you would use to extend
9
- # Authlogic. That being said, they are great examples of how to extend Authlogic and
10
- # add / modify behavior to Authlogic. These modules could easily be pulled out into
11
- # their own plugin and become an "add on" without any change.
12
- #
13
- # Now to the point of this module. Just like in ActiveRecord you have before_save,
14
- # before_validation, etc. You have similar callbacks with Authlogic, see the METHODS
15
- # constant below. The order of execution is as follows:
16
- #
17
- # before_persisting
18
- # persist
19
- # after_persisting
20
- # [save record if record.changed?]
21
- #
22
- # before_validation
23
- # before_validation_on_create
24
- # before_validation_on_update
25
- # validate
26
- # after_validation_on_update
27
- # after_validation_on_create
28
- # after_validation
29
- # [save record if record.changed?]
30
- #
31
- # before_save
32
- # before_create
33
- # before_update
34
- # after_update
35
- # after_create
36
- # after_save
37
- # [save record if record.changed?]
38
- #
39
- # before_destroy
40
- # [save record if record.changed?]
41
- # destroy
42
- # after_destroy
43
- #
44
- # Notice the "save record if changed?" lines above. This helps with performance. If
45
- # you need to make changes to the associated record, there is no need to save the
46
- # record, Authlogic will do it for you. This allows multiple modules to modify the
47
- # record and execute as few queries as possible.
48
- #
49
- # **WARNING**: unlike ActiveRecord, these callbacks must be set up on the class level:
50
- #
51
- # class UserSession < Authlogic::Session::Base
52
- # before_validation :my_method
53
- # validate :another_method
54
- # # ..etc
55
- # end
56
- #
57
- # You can NOT define a "before_validation" method, this is bad practice and does not
58
- # allow Authlogic to extend properly with multiple extensions. Please ONLY use the
59
- # method above.
60
- module Callbacks
61
- METHODS = %w[
62
- before_persisting
63
- persist
64
- after_persisting
65
- before_validation
66
- before_validation_on_create
67
- before_validation_on_update
68
- validate
69
- after_validation_on_update
70
- after_validation_on_create
71
- after_validation
72
- before_save
73
- before_create
74
- before_update
75
- after_update
76
- after_create
77
- after_save
78
- before_destroy
79
- after_destroy
80
- ].freeze
81
-
82
- class << self
83
- def included(base) #:nodoc:
84
- base.send :include, ActiveSupport::Callbacks
85
- define_session_callbacks(base)
86
- define_session_callback_installation_methods(base)
87
- end
88
-
89
- private
90
-
91
- # Defines the "callback installation methods". Other modules will use
92
- # these class methods to install their callbacks. Examples:
93
- #
94
- # ```
95
- # # session/timeout.rb, in `included`
96
- # before_persisting :reset_stale_state
97
- #
98
- # # session/password.rb, in `included`
99
- # validate :validate_by_password, if: :authenticating_with_password?
100
- # ```
101
- def define_session_callback_installation_methods(base)
102
- METHODS.each do |method|
103
- base.class_eval <<-EOS, __FILE__, __LINE__ + 1
104
- def self.#{method}(*filter_list, &block)
105
- set_callback(:#{method}, *filter_list, &block)
106
- end
107
- EOS
108
- end
109
- end
110
-
111
- # Defines session life cycle events that support callbacks.
112
- def define_session_callbacks(base)
113
- if Gem::Version.new(ActiveSupport::VERSION::STRING) >= Gem::Version.new("5")
114
- base.define_callbacks(
115
- *METHODS,
116
- terminator: ->(_target, result_lambda) { result_lambda.call == false }
117
- )
118
- base.define_callbacks(
119
- "persist",
120
- terminator: ->(_target, result_lambda) { result_lambda.call == true }
121
- )
122
- else
123
- base.define_callbacks(
124
- *METHODS,
125
- terminator: ->(_target, result) { result == false }
126
- )
127
- base.define_callbacks(
128
- "persist",
129
- terminator: ->(_target, result) { result == true }
130
- )
131
- end
132
- end
133
- end
134
-
135
- METHODS.each do |method|
136
- class_eval(
137
- <<-EOS, __FILE__, __LINE__ + 1
138
- def #{method}
139
- run_callbacks(:#{method})
140
- end
141
- EOS
142
- )
143
- end
144
-
145
- def save_record(alternate_record = nil)
146
- r = alternate_record || record
147
- if r&.changed? && !r.readonly?
148
- r.save_without_session_maintenance(validate: false)
149
- end
150
- end
151
- end
152
- end
153
- end
@@ -1,329 +0,0 @@
1
- module Authlogic
2
- module Session
3
- # Handles all authentication that deals with cookies, such as persisting,
4
- # saving, and destroying.
5
- module Cookies
6
- VALID_SAME_SITE_VALUES = [nil, "Lax", "Strict", "None"].freeze
7
-
8
- def self.included(klass)
9
- klass.class_eval do
10
- extend Config
11
- include InstanceMethods
12
- persist :persist_by_cookie
13
- after_save :save_cookie
14
- after_destroy :destroy_cookie
15
- end
16
- end
17
-
18
- # Configuration for the cookie feature set.
19
- module Config
20
- # The name of the cookie or the key in the cookies hash. Be sure and use
21
- # a unique name. If you have multiple sessions and they use the same
22
- # cookie it will cause problems. Also, if a id is set it will be
23
- # inserted into the beginning of the string. Example:
24
- #
25
- # session = UserSession.new
26
- # session.cookie_key => "user_credentials"
27
- #
28
- # session = UserSession.new(:super_high_secret)
29
- # session.cookie_key => "super_high_secret_user_credentials"
30
- #
31
- # * <tt>Default:</tt> "#{klass_name.underscore}_credentials"
32
- # * <tt>Accepts:</tt> String
33
- def cookie_key(value = nil)
34
- rw_config(:cookie_key, value, "#{klass_name.underscore}_credentials")
35
- end
36
- alias_method :cookie_key=, :cookie_key
37
-
38
- # If sessions should be remembered by default or not.
39
- #
40
- # * <tt>Default:</tt> false
41
- # * <tt>Accepts:</tt> Boolean
42
- def remember_me(value = nil)
43
- rw_config(:remember_me, value, false)
44
- end
45
- alias_method :remember_me=, :remember_me
46
-
47
- # The length of time until the cookie expires.
48
- #
49
- # * <tt>Default:</tt> 3.months
50
- # * <tt>Accepts:</tt> Integer, length of time in seconds, such as 60 or 3.months
51
- def remember_me_for(value = nil)
52
- rw_config(:remember_me_for, value, 3.months)
53
- end
54
- alias_method :remember_me_for=, :remember_me_for
55
-
56
- # Should the cookie be set as secure? If true, the cookie will only be sent over
57
- # SSL connections
58
- #
59
- # * <tt>Default:</tt> true
60
- # * <tt>Accepts:</tt> Boolean
61
- def secure(value = nil)
62
- rw_config(:secure, value, true)
63
- end
64
- alias_method :secure=, :secure
65
-
66
- # Should the cookie be set as httponly? If true, the cookie will not be
67
- # accessible from javascript
68
- #
69
- # * <tt>Default:</tt> true
70
- # * <tt>Accepts:</tt> Boolean
71
- def httponly(value = nil)
72
- rw_config(:httponly, value, true)
73
- end
74
- alias_method :httponly=, :httponly
75
-
76
- # Should the cookie be prevented from being send along with cross-site
77
- # requests?
78
- #
79
- # * <tt>Default:</tt> nil
80
- # * <tt>Accepts:</tt> String, one of nil, 'Lax' or 'Strict'
81
- def same_site(value = nil)
82
- unless VALID_SAME_SITE_VALUES.include?(value)
83
- msg = "Invalid same_site value: #{value}. Valid: #{VALID_SAME_SITE_VALUES.inspect}"
84
- raise ArgumentError.new(msg)
85
- end
86
- rw_config(:same_site, value)
87
- end
88
- alias_method :same_site=, :same_site
89
-
90
- # Should the cookie be signed? If the controller adapter supports it, this is a
91
- # measure against cookie tampering.
92
- def sign_cookie(value = nil)
93
- if value && !controller.cookies.respond_to?(:signed)
94
- raise "Signed cookies not supported with #{controller.class}!"
95
- end
96
- rw_config(:sign_cookie, value, false)
97
- end
98
- alias_method :sign_cookie=, :sign_cookie
99
-
100
- # Should the cookie be encrypted? If the controller adapter supports it, this is a
101
- # measure to hide the contents of the cookie (e.g. persistence_token)"
102
- def encrypt_cookie(value = nil)
103
- if value && !controller.cookies.respond_to?(:encrypted)
104
- raise "Encrypted cookies not supported with #{controller.class}!"
105
- end
106
- if value && sign_cookie
107
- raise "It is recommended to use encrypt_cookie instead of sign_cookie. " \
108
- "You may not enable both options."
109
- end
110
- rw_config(:encrypt_cookie, value, false)
111
- end
112
- alias_method :encrypt_cookie=, :encrypt_cookie
113
- end
114
-
115
- # The methods available for an Authlogic::Session::Base object that make up the
116
- # cookie feature set.
117
- module InstanceMethods
118
- # Allows you to set the remember_me option when passing credentials.
119
- def credentials=(value)
120
- super
121
- values = value.is_a?(Array) ? value : [value]
122
- case values.first
123
- when Hash
124
- if values.first.with_indifferent_access.key?(:remember_me)
125
- self.remember_me = values.first.with_indifferent_access[:remember_me]
126
- end
127
- else
128
- r = values.find { |val| val.is_a?(TrueClass) || val.is_a?(FalseClass) }
129
- self.remember_me = r unless r.nil?
130
- end
131
- end
132
-
133
- # Is the cookie going to expire after the session is over, or will it stick around?
134
- def remember_me
135
- return @remember_me if defined?(@remember_me)
136
- @remember_me = self.class.remember_me
137
- end
138
-
139
- # Accepts a boolean as a flag to remember the session or not. Basically
140
- # to expire the cookie at the end of the session or keep it for
141
- # "remember_me_until".
142
- def remember_me=(value)
143
- @remember_me = value
144
- end
145
-
146
- # See remember_me
147
- def remember_me?
148
- remember_me == true || remember_me == "true" || remember_me == "1"
149
- end
150
-
151
- # How long to remember the user if remember_me is true. This is based on the class
152
- # level configuration: remember_me_for
153
- def remember_me_for
154
- return unless remember_me?
155
- self.class.remember_me_for
156
- end
157
-
158
- # When to expire the cookie. See remember_me_for configuration option to change
159
- # this.
160
- def remember_me_until
161
- return unless remember_me?
162
- remember_me_for.from_now
163
- end
164
-
165
- # Has the cookie expired due to current time being greater than remember_me_until.
166
- def remember_me_expired?
167
- return unless remember_me?
168
- (Time.parse(cookie_credentials[2]) < Time.now)
169
- end
170
-
171
- # If the cookie should be marked as secure (SSL only)
172
- def secure
173
- return @secure if defined?(@secure)
174
- @secure = self.class.secure
175
- end
176
-
177
- # Accepts a boolean as to whether the cookie should be marked as secure. If true
178
- # the cookie will only ever be sent over an SSL connection.
179
- def secure=(value)
180
- @secure = value
181
- end
182
-
183
- # See secure
184
- def secure?
185
- secure == true || secure == "true" || secure == "1"
186
- end
187
-
188
- # If the cookie should be marked as httponly (not accessible via javascript)
189
- def httponly
190
- return @httponly if defined?(@httponly)
191
- @httponly = self.class.httponly
192
- end
193
-
194
- # Accepts a boolean as to whether the cookie should be marked as
195
- # httponly. If true, the cookie will not be accessible from javascript
196
- def httponly=(value)
197
- @httponly = value
198
- end
199
-
200
- # See httponly
201
- def httponly?
202
- httponly == true || httponly == "true" || httponly == "1"
203
- end
204
-
205
- # If the cookie should be marked as SameSite with 'Lax' or 'Strict' flag.
206
- def same_site
207
- return @same_site if defined?(@same_site)
208
- @same_site = self.class.same_site(nil)
209
- end
210
-
211
- # Accepts nil, 'Lax' or 'Strict' as possible flags.
212
- def same_site=(value)
213
- unless VALID_SAME_SITE_VALUES.include?(value)
214
- msg = "Invalid same_site value: #{value}. Valid: #{VALID_SAME_SITE_VALUES.inspect}"
215
- raise ArgumentError.new(msg)
216
- end
217
- @same_site = value
218
- end
219
-
220
- # If the cookie should be signed
221
- def sign_cookie
222
- return @sign_cookie if defined?(@sign_cookie)
223
- @sign_cookie = self.class.sign_cookie
224
- end
225
-
226
- # Accepts a boolean as to whether the cookie should be signed. If true
227
- # the cookie will be saved and verified using a signature.
228
- def sign_cookie=(value)
229
- @sign_cookie = value
230
- end
231
-
232
- # See sign_cookie
233
- def sign_cookie?
234
- sign_cookie == true || sign_cookie == "true" || sign_cookie == "1"
235
- end
236
-
237
- # If the cookie should be encrypted
238
- def encrypt_cookie
239
- return @encrypt_cookie if defined?(@encrypt_cookie)
240
- @encrypt_cookie = self.class.encrypt_cookie
241
- end
242
-
243
- # Accepts a boolean as to whether the cookie should be encrypted. If true
244
- # the cookie will be saved in an encrypted state.
245
- def encrypt_cookie=(value)
246
- @encrypt_cookie = value
247
- end
248
-
249
- # See encrypt_cookie
250
- def encrypt_cookie?
251
- encrypt_cookie == true || encrypt_cookie == "true" || encrypt_cookie == "1"
252
- end
253
-
254
- private
255
-
256
- def cookie_key
257
- build_key(self.class.cookie_key)
258
- end
259
-
260
- # Returns an array of cookie elements. See cookie format in
261
- # `generate_cookie_for_saving`. If no cookie is found, returns nil.
262
- def cookie_credentials
263
- cookie = cookie_jar[cookie_key]
264
- cookie&.split("::")
265
- end
266
-
267
- # The third element of the cookie indicates whether the user wanted
268
- # to be remembered (Actually, it's a timestamp, `remember_me_until`)
269
- # See cookie format in `generate_cookie_for_saving`.
270
- def cookie_credentials_remember_me?
271
- !cookie_credentials.nil? && !cookie_credentials[2].nil?
272
- end
273
-
274
- def cookie_jar
275
- if self.class.encrypt_cookie
276
- controller.cookies.encrypted
277
- elsif self.class.sign_cookie
278
- controller.cookies.signed
279
- else
280
- controller.cookies
281
- end
282
- end
283
-
284
- # Tries to validate the session from information in the cookie
285
- def persist_by_cookie
286
- persistence_token, record_id = cookie_credentials
287
- if persistence_token.present?
288
- record = search_for_record("find_by_#{klass.primary_key}", record_id)
289
- if record && record.persistence_token == persistence_token
290
- self.unauthorized_record = record
291
- end
292
- valid?
293
- else
294
- false
295
- end
296
- end
297
-
298
- def save_cookie
299
- cookie_jar[cookie_key] = generate_cookie_for_saving
300
- true
301
- end
302
-
303
- def generate_cookie_for_saving
304
- {
305
- value: generate_cookie_value,
306
- expires: remember_me_until,
307
- secure: secure,
308
- httponly: httponly,
309
- same_site: same_site,
310
- domain: controller.cookie_domain
311
- }
312
- end
313
-
314
- def generate_cookie_value
315
- format(
316
- "%s::%s%s",
317
- record.persistence_token.to_s,
318
- record.send(record.class.primary_key).to_s,
319
- remember_me? ? "::#{remember_me_until.iso8601}" : ""
320
- )
321
- end
322
-
323
- def destroy_cookie
324
- controller.cookies.delete cookie_key, domain: controller.cookie_domain
325
- end
326
- end
327
- end
328
- end
329
- end