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
@@ -4,12 +4,25 @@ module Authlogic
4
4
  module ActsAsAuthentic
5
5
  module Queries
6
6
  # The query used by public-API method `find_by_smart_case_login_field`.
7
+ #
8
+ # We use the rails methods `case_insensitive_comparison` and
9
+ # `case_sensitive_comparison`. These methods nicely take into account
10
+ # MySQL collations. (Consider the case where a user *says* they want a
11
+ # case-sensitive uniqueness validation, but then they configure their
12
+ # database to have an insensitive collation. Rails will handle this for
13
+ # us, by downcasing, see
14
+ # `active_record/connection_adapters/abstract_mysql_adapter.rb`) So that's
15
+ # great! But, these methods are not part of rails' public API, so there
16
+ # are no docs. So, everything we know about how to use the methods
17
+ # correctly comes from mimicing what we find in
18
+ # `active_record/validations/uniqueness.rb`.
19
+ #
7
20
  # @api private
8
21
  class FindWithCase
9
22
  # Dup ActiveRecord.gem_version before freezing, in case someone
10
23
  # else wants to modify it. Freezing modifies an object in place.
11
24
  # https://github.com/binarylogic/authlogic/pull/590
12
- AR_GEM_VERSION = ActiveRecord.gem_version.dup.freeze
25
+ AR_GEM_VERSION = ::ActiveRecord.gem_version.dup.freeze
13
26
 
14
27
  # @api private
15
28
  def initialize(model_class, field, value, sensitive)
@@ -21,44 +34,47 @@ module Authlogic
21
34
 
22
35
  # @api private
23
36
  def execute
24
- bind(relation).first
37
+ @model_class.where(comparison).first
25
38
  end
26
39
 
27
40
  private
28
41
 
29
42
  # @api private
30
- def bind(relation)
31
- if AR_GEM_VERSION >= Gem::Version.new("5")
32
- bind = ActiveRecord::Relation::QueryAttribute.new(
33
- @field,
34
- @value,
35
- ActiveRecord::Type::Value.new
36
- )
37
- @model_class.where(relation, bind)
38
- else
39
- @model_class.where(relation)
40
- end
43
+ # @return Arel::Nodes::Equality
44
+ def comparison
45
+ @sensitive ? sensitive_comparison : insensitive_comparison
41
46
  end
42
47
 
43
48
  # @api private
44
- def relation
45
- if !@sensitive
49
+ def insensitive_comparison
50
+ if AR_GEM_VERSION > Gem::Version.new("5.3")
51
+ @model_class.connection.case_insensitive_comparison(
52
+ @model_class.arel_table[@field], @value
53
+ )
54
+ else
46
55
  @model_class.connection.case_insensitive_comparison(
47
56
  @model_class.arel_table,
48
57
  @field,
49
58
  @model_class.columns_hash[@field],
50
59
  @value
51
60
  )
52
- elsif AR_GEM_VERSION >= Gem::Version.new("5.0")
61
+ end
62
+ end
63
+
64
+ # @api private
65
+ def sensitive_comparison
66
+ bound_value = @model_class.predicate_builder.build_bind_attribute(@field, @value)
67
+ if AR_GEM_VERSION > Gem::Version.new("5.3")
68
+ @model_class.connection.case_sensitive_comparison(
69
+ @model_class.arel_table[@field], bound_value
70
+ )
71
+ else
53
72
  @model_class.connection.case_sensitive_comparison(
54
73
  @model_class.arel_table,
55
74
  @field,
56
75
  @model_class.columns_hash[@field],
57
- @value
76
+ bound_value
58
77
  )
59
- else
60
- value = @model_class.connection.case_sensitive_modifier(@value, @field)
61
- @model_class.arel_table[@field].eq(value)
62
78
  end
63
79
  end
64
80
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Authlogic
2
4
  module ActsAsAuthentic
3
5
  # This is one of my favorite features that I think is pretty cool. It's
@@ -40,7 +42,7 @@ module Authlogic
40
42
  def log_in_after_create(value = nil)
41
43
  rw_config(:log_in_after_create, value, true)
42
44
  end
43
- alias_method :log_in_after_create=, :log_in_after_create
45
+ alias log_in_after_create= log_in_after_create
44
46
 
45
47
  # In order to turn off automatic maintenance of sessions when updating
46
48
  # the password, just set this to false.
@@ -50,7 +52,7 @@ module Authlogic
50
52
  def log_in_after_password_change(value = nil)
51
53
  rw_config(:log_in_after_password_change, value, true)
52
54
  end
53
- alias_method :log_in_after_password_change=, :log_in_after_password_change
55
+ alias log_in_after_password_change= log_in_after_password_change
54
56
 
55
57
  # As you may know, authlogic sessions can be separate by id (See
56
58
  # Authlogic::Session::Base#id). You can specify here what session ids
@@ -62,7 +64,7 @@ module Authlogic
62
64
  def session_ids(value = nil)
63
65
  rw_config(:session_ids, value, [nil])
64
66
  end
65
- alias_method :session_ids=, :session_ids
67
+ alias session_ids= session_ids
66
68
 
67
69
  # The name of the associated session class. This is inferred by the name
68
70
  # of the model.
@@ -77,7 +79,7 @@ module Authlogic
77
79
  end
78
80
  rw_config(:session_class, value, const)
79
81
  end
80
- alias_method :session_class=, :session_class
82
+ alias session_class= session_class
81
83
  end
82
84
 
83
85
  # This module, as one of the `acts_as_authentic_modules`, is only included
@@ -91,9 +93,9 @@ module Authlogic
91
93
  end
92
94
 
93
95
  # Save the record and skip session maintenance all together.
94
- def save_without_session_maintenance(*args)
96
+ def save_without_session_maintenance(**options)
95
97
  self.skip_session_maintenance = true
96
- result = save(*args)
98
+ result = save(**options)
97
99
  self.skip_session_maintenance = false
98
100
  result
99
101
  end
@@ -114,7 +116,7 @@ module Authlogic
114
116
  session_class.activated? &&
115
117
  maintain_session? &&
116
118
  !session_ids.blank? &&
117
- persistence_token_changed?
119
+ will_save_change_to_persistence_token?
118
120
  end
119
121
 
120
122
  def maintain_session?
@@ -174,7 +176,9 @@ module Authlogic
174
176
  end
175
177
 
176
178
  def log_in_after_password_change?
177
- persistence_token_changed? && self.class.log_in_after_password_change
179
+ persisted? &&
180
+ will_save_change_to_persistence_token? &&
181
+ self.class.log_in_after_password_change
178
182
  end
179
183
  end
180
184
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Authlogic
2
4
  module ActsAsAuthentic
3
- # This module is responsible for maintaining the single_access token. For more
4
- # information the single access token and how to use it, see the
5
- # Authlogic::Session::Params module.
5
+ # This module is responsible for maintaining the single_access token. For
6
+ # more information the single access token and how to use it, see "Params"
7
+ # in `Session::Base`.
6
8
  module SingleAccessToken
7
9
  def self.included(klass)
8
10
  klass.class_eval do
@@ -25,10 +27,7 @@ module Authlogic
25
27
  def change_single_access_token_with_password(value = nil)
26
28
  rw_config(:change_single_access_token_with_password, value, false)
27
29
  end
28
- alias_method(
29
- :change_single_access_token_with_password=,
30
- :change_single_access_token_with_password
31
- )
30
+ alias change_single_access_token_with_password= change_single_access_token_with_password
32
31
  end
33
32
 
34
33
  # All method, for the single_access token aspect of acts_as_authentic.
@@ -41,7 +40,10 @@ module Authlogic
41
40
 
42
41
  klass.class_eval do
43
42
  include InstanceMethods
44
- validates_uniqueness_of :single_access_token, if: :single_access_token_changed?
43
+ validates_uniqueness_of :single_access_token,
44
+ case_sensitive: true,
45
+ if: :will_save_change_to_single_access_token?
46
+
45
47
  before_validation :reset_single_access_token, if: :reset_single_access_token?
46
48
  if respond_to?(:after_password_set)
47
49
  after_password_set(
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Authlogic
4
+ # Mixed into `Authlogic::ActsAsAuthentic::Base` and
5
+ # `Authlogic::Session::Base`.
2
6
  module Config
3
- E_USE_NORMAL_RAILS_VALIDATION = <<~EOS.freeze
7
+ E_USE_NORMAL_RAILS_VALIDATION = <<~EOS
4
8
  This Authlogic configuration option (%s) is deprecated. Use normal
5
9
  ActiveRecord validation instead. Detailed instructions:
6
10
  https://github.com/binarylogic/authlogic/blob/master/doc/use_normal_rails_validation.md
@@ -8,6 +12,10 @@ module Authlogic
8
12
 
9
13
  def self.extended(klass)
10
14
  klass.class_eval do
15
+ # TODO: Is this a confusing name, given this module is mixed into
16
+ # both `Authlogic::ActsAsAuthentic::Base` and
17
+ # `Authlogic::Session::Base`? Perhaps a more generic name, like
18
+ # `authlogic_config` would be better?
11
19
  class_attribute :acts_as_authentic_config
12
20
  self.acts_as_authentic_config ||= {}
13
21
  end
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Authlogic
2
4
  module ControllerAdapters # :nodoc:
3
- # Allows you to use Authlogic in any framework you want, not just rails. See the RailsAdapter
4
- # for an example of how to adapt Authlogic to work with your framework.
5
+ # Allows you to use Authlogic in any framework you want, not just rails. See
6
+ # the RailsAdapter for an example of how to adapt Authlogic to work with
7
+ # your framework.
5
8
  class AbstractAdapter
6
9
  E_COOKIE_DOMAIN_ADAPTER = "The cookie_domain method has not been " \
7
- "implemented by the controller adapter".freeze
10
+ "implemented by the controller adapter"
11
+ ENV_SESSION_OPTIONS = "rack.session.options"
8
12
 
9
13
  attr_accessor :controller
10
14
 
@@ -26,7 +30,7 @@ module Authlogic
26
30
  end
27
31
 
28
32
  def cookie_domain
29
- raise NotImplementedError.new(E_COOKIE_DOMAIN_ADAPTER)
33
+ raise NotImplementedError, E_COOKIE_DOMAIN_ADAPTER
30
34
  end
31
35
 
32
36
  def params
@@ -41,6 +45,26 @@ module Authlogic
41
45
  request.content_type
42
46
  end
43
47
 
48
+ # Inform Rack that we would like a new session ID to be assigned. Changes
49
+ # the ID, but not the contents of the session.
50
+ #
51
+ # The `:renew` option is read by `rack/session/abstract/id.rb`.
52
+ #
53
+ # This is how Devise (via warden) implements defense against Session
54
+ # Fixation. Our implementation is copied directly from the warden gem
55
+ # (set_user in warden/proxy.rb)
56
+ def renew_session_id
57
+ env = request.env
58
+ options = env[ENV_SESSION_OPTIONS]
59
+ if options
60
+ if options.frozen?
61
+ env[ENV_SESSION_OPTIONS] = options.merge(renew: true).freeze
62
+ else
63
+ options[:renew] = true
64
+ end
65
+ end
66
+ end
67
+
44
68
  def session
45
69
  controller.session
46
70
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Authlogic
2
4
  module ControllerAdapters
3
5
  # Adapter for authlogic to make it function as a Rack middleware.
@@ -1,4 +1,4 @@
1
- require "action_controller"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Authlogic
4
4
  module ControllerAdapters
@@ -7,8 +7,6 @@ module Authlogic
7
7
  # Similar to how ActiveRecord has an adapter for MySQL, PostgreSQL, SQLite,
8
8
  # etc.
9
9
  class RailsAdapter < AbstractAdapter
10
- class AuthlogicLoadedTooLateError < StandardError; end
11
-
12
10
  def authenticate_with_http_basic(&block)
13
11
  controller.authenticate_with_http_basic(&block)
14
12
  end
@@ -16,12 +14,11 @@ module Authlogic
16
14
  # Returns a `ActionDispatch::Cookies::CookieJar`. See the AC guide
17
15
  # http://guides.rubyonrails.org/action_controller_overview.html#cookies
18
16
  def cookies
19
- controller.send(:cookies)
17
+ controller.respond_to?(:cookies, true) ? controller.send(:cookies) : nil
20
18
  end
21
19
 
22
20
  def cookie_domain
23
- @cookie_domain_key ||= Rails::VERSION::STRING >= "2.3" ? :domain : :session_domain
24
- controller.request.session_options[@cookie_domain_key]
21
+ controller.request.session_options[:domain]
25
22
  end
26
23
 
27
24
  def request_content_type
@@ -32,26 +29,7 @@ module Authlogic
32
29
  # "activates" authlogic.
33
30
  module RailsImplementation
34
31
  def self.included(klass) # :nodoc:
35
- if defined?(::ApplicationController)
36
- raise AuthlogicLoadedTooLateError.new(
37
- <<~EOS.squish
38
- Authlogic is trying to add a callback to ActionController::Base
39
- but ApplicationController has already been loaded, so the
40
- callback won't be copied into your application. Generally this
41
- is due to another gem or plugin requiring your
42
- ApplicationController prematurely, such as the
43
- resource_controller plugin. Please require Authlogic first,
44
- before these other gems / plugins.
45
- EOS
46
- )
47
- end
48
-
49
- # In Rails 4.0.2, the *_filter methods were renamed to *_action.
50
- if klass.respond_to? :prepend_before_action
51
- klass.prepend_before_action :activate_authlogic
52
- else
53
- klass.prepend_before_filter :activate_authlogic
54
- end
32
+ klass.prepend_before_action :activate_authlogic
55
33
  end
56
34
 
57
35
  private
@@ -64,7 +42,6 @@ module Authlogic
64
42
  end
65
43
  end
66
44
 
67
- ActionController::Base.send(
68
- :include,
69
- Authlogic::ControllerAdapters::RailsAdapter::RailsImplementation
70
- )
45
+ ActiveSupport.on_load(:action_controller) do
46
+ include Authlogic::ControllerAdapters::RailsAdapter::RailsImplementation
47
+ end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Authlogic bridge for Sinatra
2
4
  module Authlogic
3
5
  module ControllerAdapters
4
6
  module SinatraAdapter
7
+ # Cookie management functions
5
8
  class Cookies
6
9
  attr_reader :request, :response
7
10
 
@@ -23,6 +26,7 @@ module Authlogic
23
26
  end
24
27
  end
25
28
 
29
+ # Thin wrapper around request and response.
26
30
  class Controller
27
31
  attr_reader :request, :response, :cookies
28
32
 
@@ -40,11 +44,13 @@ module Authlogic
40
44
  end
41
45
  end
42
46
 
47
+ # Sinatra controller adapter
43
48
  class Adapter < AbstractAdapter
44
49
  def cookie_domain
45
50
  env["SERVER_NAME"]
46
51
  end
47
52
 
53
+ # Mixed into `Sinatra::Base`
48
54
  module Implementation
49
55
  def self.included(klass)
50
56
  klass.send :before do
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Authlogic
4
+ # Represents the credentials *in* the cookie. The value of the cookie.
5
+ # This is primarily a data object. It doesn't interact with controllers.
6
+ # It doesn't know about eg. cookie expiration.
7
+ #
8
+ # @api private
9
+ class CookieCredentials
10
+ # @api private
11
+ class ParseError < RuntimeError
12
+ end
13
+
14
+ DELIMITER = "::"
15
+
16
+ attr_reader :persistence_token, :record_id, :remember_me_until
17
+
18
+ # @api private
19
+ # @param persistence_token [String]
20
+ # @param record_id [String, Numeric]
21
+ # @param remember_me_until [ActiveSupport::TimeWithZone]
22
+ def initialize(persistence_token, record_id, remember_me_until)
23
+ @persistence_token = persistence_token
24
+ @record_id = record_id
25
+ @remember_me_until = remember_me_until
26
+ end
27
+
28
+ class << self
29
+ # @api private
30
+ def parse(string)
31
+ parts = string.split(DELIMITER)
32
+ unless (1..3).cover?(parts.length)
33
+ raise ParseError, format("Expected 1..3 parts, got %d", parts.length)
34
+ end
35
+ new(parts[0], parts[1], parse_time(parts[2]))
36
+ end
37
+
38
+ private
39
+
40
+ # @api private
41
+ def parse_time(string)
42
+ return if string.nil?
43
+ ::Time.parse(string)
44
+ rescue ::ArgumentError => e
45
+ raise ParseError, format("Found cookie, cannot parse remember_me_until: #{e}")
46
+ end
47
+ end
48
+
49
+ # @api private
50
+ def remember_me?
51
+ !@remember_me_until.nil?
52
+ end
53
+
54
+ # @api private
55
+ def to_s
56
+ [
57
+ @persistence_token,
58
+ @record_id.to_s,
59
+ @remember_me_until&.iso8601
60
+ ].compact.join(DELIMITER)
61
+ end
62
+ end
63
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bcrypt"
2
4
 
3
5
  module Authlogic
@@ -64,10 +66,8 @@ module Authlogic
64
66
 
65
67
  def cost=(val)
66
68
  if val < ::BCrypt::Engine::MIN_COST
67
- raise ArgumentError.new(
68
- "Authlogic's bcrypt cost cannot be set below the engine's " \
69
+ raise ArgumentError, "Authlogic's bcrypt cost cannot be set below the engine's " \
69
70
  "min cost (#{::BCrypt::Engine::MIN_COST})"
70
- )
71
71
  end
72
72
  @cost = val
73
73
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/md5"
4
+
5
+ module Authlogic
6
+ module CryptoProviders
7
+ class MD5
8
+ # A poor choice. There are known attacks against this algorithm.
9
+ class V2
10
+ class << self
11
+ attr_accessor :join_token
12
+
13
+ # The number of times to loop through the encryption.
14
+ def stretches
15
+ @stretches ||= 1
16
+ end
17
+ attr_writer :stretches
18
+
19
+ # Turns your raw password into a MD5 hash.
20
+ def encrypt(*tokens)
21
+ digest = tokens.flatten.join(join_token)
22
+ stretches.times { digest = Digest::MD5.digest(digest) }
23
+ digest.unpack1("H*")
24
+ end
25
+
26
+ # Does the crypted password match the tokens? Uses the same tokens that
27
+ # were used to encrypt.
28
+ def matches?(crypted, *tokens)
29
+ encrypt(*tokens) == crypted
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,14 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "digest/md5"
2
4
 
3
5
  module Authlogic
4
6
  module CryptoProviders
5
- # This class was made for the users transitioning from md5 based systems.
6
- # I highly discourage using this crypto provider as it superbly inferior
7
- # to your other options.
8
- #
9
- # Please use any other provider offered by Authlogic (except AES256, that
10
- # would be even worse).
7
+ # A poor choice. There are known attacks against this algorithm.
11
8
  class MD5
9
+ # V2 hashes the digest bytes in repeated stretches instead of hex characters.
10
+ autoload :V2, File.join(__dir__, "md5", "v2")
11
+
12
12
  class << self
13
13
  attr_accessor :join_token
14
14
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "scrypt"
2
4
 
3
5
  module Authlogic
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha1"
4
+
5
+ module Authlogic
6
+ module CryptoProviders
7
+ class Sha1
8
+ # A poor choice. There are known attacks against this algorithm.
9
+ class V2
10
+ class << self
11
+ def join_token
12
+ @join_token ||= "--"
13
+ end
14
+ attr_writer :join_token
15
+
16
+ # The number of times to loop through the encryption.
17
+ def stretches
18
+ @stretches ||= 10
19
+ end
20
+ attr_writer :stretches
21
+
22
+ # Turns your raw password into a Sha1 hash.
23
+ def encrypt(*tokens)
24
+ tokens = tokens.flatten
25
+ digest = tokens.shift
26
+ stretches.times do
27
+ digest = Digest::SHA1.digest([digest, *tokens].join(join_token))
28
+ end
29
+ digest.unpack1("H*")
30
+ end
31
+
32
+ # Does the crypted password match the tokens? Uses the same tokens that
33
+ # were used to encrypt.
34
+ def matches?(crypted, *tokens)
35
+ encrypt(*tokens) == crypted
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "digest/sha1"
2
4
 
3
5
  module Authlogic
4
6
  module CryptoProviders
5
- # This class was made for the users transitioning from
6
- # restful_authentication. Use of this crypto provider is highly discouraged.
7
- # It is far inferior to your other options. Please use any other provider
8
- # offered by Authlogic.
7
+ # A poor choice. There are known attacks against this algorithm.
9
8
  class Sha1
9
+ # V2 hashes the digest bytes in repeated stretches instead of hex characters.
10
+ autoload :V2, File.join(__dir__, "sha1", "v2")
11
+
10
12
  class << self
11
13
  def join_token
12
14
  @join_token ||= "--"
13
15
  end
14
16
  attr_writer :join_token
15
17
 
16
- # The number of times to loop through the encryption. This is ten
17
- # because that is what restful_authentication defaults to.
18
+ # The number of times to loop through the encryption.
18
19
  def stretches
19
20
  @stretches ||= 10
20
21
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha2"
4
+
5
+ module Authlogic
6
+ # The acts_as_authentic method has a crypto_provider option. This allows you
7
+ # to use any type of encryption you like. Just create a class with a class
8
+ # level encrypt and matches? method. See example below.
9
+ #
10
+ # === Example
11
+ #
12
+ # class MyAwesomeEncryptionMethod
13
+ # def self.encrypt(*tokens)
14
+ # # the tokens passed will be an array of objects, what type of object
15
+ # # is irrelevant, just do what you need to do with them and return a
16
+ # # single encrypted string. for example, you will most likely join all
17
+ # # of the objects into a single string and then encrypt that string
18
+ # end
19
+ #
20
+ # def self.matches?(crypted, *tokens)
21
+ # # return true if the crypted string matches the tokens. Depending on
22
+ # # your algorithm you might decrypt the string then compare it to the
23
+ # # token, or you might encrypt the tokens and make sure it matches the
24
+ # # crypted string, its up to you.
25
+ # end
26
+ # end
27
+ module CryptoProviders
28
+ class Sha256
29
+ # = Sha256
30
+ #
31
+ # Uses the Sha256 hash algorithm to encrypt passwords.
32
+ class V2
33
+ class << self
34
+ attr_accessor :join_token
35
+
36
+ # The number of times to loop through the encryption.
37
+ def stretches
38
+ @stretches ||= 20
39
+ end
40
+ attr_writer :stretches
41
+
42
+ # Turns your raw password into a Sha256 hash.
43
+ def encrypt(*tokens)
44
+ digest = tokens.flatten.join(join_token)
45
+ stretches.times { digest = Digest::SHA256.digest(digest) }
46
+ digest.unpack1("H*")
47
+ end
48
+
49
+ # Does the crypted password match the tokens? Uses the same tokens that
50
+ # were used to encrypt.
51
+ def matches?(crypted, *tokens)
52
+ encrypt(*tokens) == crypted
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "digest/sha2"
2
4
 
3
5
  module Authlogic
@@ -27,6 +29,9 @@ module Authlogic
27
29
  #
28
30
  # Uses the Sha256 hash algorithm to encrypt passwords.
29
31
  class Sha256
32
+ # V2 hashes the digest bytes in repeated stretches instead of hex characters.
33
+ autoload :V2, File.join(__dir__, "sha256", "v2")
34
+
30
35
  class << self
31
36
  attr_accessor :join_token
32
37