authlogic 3.8.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
- data/.github/ISSUE_TEMPLATE/feature_proposal.md +32 -0
- data/.github/triage.md +86 -0
- data/.gitignore +4 -3
- data/.rubocop.yml +109 -9
- data/.rubocop_todo.yml +38 -355
- data/.travis.yml +11 -35
- data/CHANGELOG.md +345 -2
- data/CONTRIBUTING.md +45 -14
- data/Gemfile +3 -2
- data/README.md +244 -90
- data/Rakefile +10 -10
- data/UPGRADING.md +22 -0
- data/authlogic.gemspec +34 -21
- data/doc/use_normal_rails_validation.md +82 -0
- data/gemfiles/Gemfile.rails-4.2.x +6 -0
- data/{test/gemfiles → gemfiles}/Gemfile.rails-5.1.x +2 -2
- data/{test/gemfiles → gemfiles}/Gemfile.rails-5.2.x +2 -2
- data/lib/authlogic/acts_as_authentic/base.rb +36 -24
- data/lib/authlogic/acts_as_authentic/email.rb +65 -31
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +14 -9
- data/lib/authlogic/acts_as_authentic/login.rb +61 -45
- data/lib/authlogic/acts_as_authentic/magic_columns.rb +6 -6
- data/lib/authlogic/acts_as_authentic/password.rb +267 -146
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +24 -19
- data/lib/authlogic/acts_as_authentic/persistence_token.rb +10 -15
- data/lib/authlogic/acts_as_authentic/queries/find_with_case.rb +67 -0
- data/lib/authlogic/acts_as_authentic/restful_authentication.rb +50 -14
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +88 -60
- data/lib/authlogic/acts_as_authentic/single_access_token.rb +23 -11
- data/lib/authlogic/acts_as_authentic/validations_scope.rb +9 -6
- data/lib/authlogic/authenticates_many/association.rb +7 -7
- data/lib/authlogic/authenticates_many/base.rb +37 -21
- data/lib/authlogic/config.rb +21 -10
- data/lib/authlogic/controller_adapters/abstract_adapter.rb +38 -11
- data/lib/authlogic/controller_adapters/rack_adapter.rb +9 -5
- data/lib/authlogic/controller_adapters/rails_adapter.rb +12 -7
- data/lib/authlogic/controller_adapters/sinatra_adapter.rb +2 -2
- data/lib/authlogic/crypto_providers/aes256.rb +37 -32
- data/lib/authlogic/crypto_providers/bcrypt.rb +21 -15
- data/lib/authlogic/crypto_providers/md5.rb +4 -2
- data/lib/authlogic/crypto_providers/scrypt.rb +22 -17
- data/lib/authlogic/crypto_providers/sha1.rb +11 -5
- data/lib/authlogic/crypto_providers/sha256.rb +13 -9
- data/lib/authlogic/crypto_providers/sha512.rb +0 -21
- data/lib/authlogic/crypto_providers/wordpress.rb +32 -3
- data/lib/authlogic/crypto_providers.rb +91 -0
- data/lib/authlogic/i18n.rb +26 -19
- data/lib/authlogic/random.rb +10 -28
- data/lib/authlogic/regex.rb +59 -28
- data/lib/authlogic/session/activation.rb +10 -7
- data/lib/authlogic/session/active_record_trickery.rb +13 -9
- data/lib/authlogic/session/base.rb +15 -4
- data/lib/authlogic/session/brute_force_protection.rb +40 -33
- data/lib/authlogic/session/callbacks.rb +94 -46
- data/lib/authlogic/session/cookies.rb +130 -45
- data/lib/authlogic/session/existence.rb +21 -11
- data/lib/authlogic/session/foundation.rb +64 -14
- data/lib/authlogic/session/http_auth.rb +35 -28
- data/lib/authlogic/session/id.rb +9 -4
- data/lib/authlogic/session/klass.rb +15 -12
- data/lib/authlogic/session/magic_columns.rb +58 -55
- data/lib/authlogic/session/magic_states.rb +25 -19
- data/lib/authlogic/session/params.rb +42 -28
- data/lib/authlogic/session/password.rb +130 -120
- data/lib/authlogic/session/perishable_token.rb +5 -4
- data/lib/authlogic/session/persistence.rb +18 -12
- data/lib/authlogic/session/priority_record.rb +15 -12
- data/lib/authlogic/session/scopes.rb +51 -32
- data/lib/authlogic/session/session.rb +38 -28
- data/lib/authlogic/session/timeout.rb +13 -13
- data/lib/authlogic/session/unauthorized_record.rb +18 -13
- data/lib/authlogic/session/validation.rb +9 -9
- data/lib/authlogic/test_case/mock_controller.rb +5 -4
- data/lib/authlogic/test_case/mock_cookie_jar.rb +47 -3
- data/lib/authlogic/test_case/mock_request.rb +6 -3
- data/lib/authlogic/test_case/rails_request_adapter.rb +3 -2
- data/lib/authlogic/test_case.rb +70 -2
- data/lib/authlogic/version.rb +21 -0
- data/lib/authlogic.rb +51 -49
- data/test/acts_as_authentic_test/base_test.rb +3 -1
- data/test/acts_as_authentic_test/email_test.rb +43 -42
- data/test/acts_as_authentic_test/logged_in_status_test.rb +6 -4
- data/test/acts_as_authentic_test/login_test.rb +77 -80
- data/test/acts_as_authentic_test/magic_columns_test.rb +3 -1
- data/test/acts_as_authentic_test/password_test.rb +51 -37
- data/test/acts_as_authentic_test/perishable_token_test.rb +13 -5
- data/test/acts_as_authentic_test/persistence_token_test.rb +7 -1
- data/test/acts_as_authentic_test/restful_authentication_test.rb +14 -3
- data/test/acts_as_authentic_test/session_maintenance_test.rb +69 -15
- data/test/acts_as_authentic_test/single_access_test.rb +3 -1
- data/test/adapter_test.rb +23 -0
- data/test/authenticates_many_test.rb +3 -1
- data/test/config_test.rb +11 -9
- data/test/crypto_provider_test/aes256_test.rb +3 -1
- data/test/crypto_provider_test/bcrypt_test.rb +3 -1
- data/test/crypto_provider_test/scrypt_test.rb +3 -1
- data/test/crypto_provider_test/sha1_test.rb +3 -1
- data/test/crypto_provider_test/sha256_test.rb +3 -1
- data/test/crypto_provider_test/sha512_test.rb +3 -1
- data/test/crypto_provider_test/wordpress_test.rb +26 -0
- data/test/fixtures/companies.yml +2 -2
- data/test/fixtures/employees.yml +1 -1
- data/test/i18n_test.rb +6 -4
- data/test/libs/affiliate.rb +2 -0
- data/test/libs/company.rb +4 -2
- data/test/libs/employee.rb +2 -0
- data/test/libs/employee_session.rb +2 -0
- data/test/libs/ldaper.rb +2 -0
- data/test/libs/project.rb +2 -0
- data/test/libs/user.rb +2 -0
- data/test/libs/user_session.rb +4 -2
- data/test/random_test.rb +10 -38
- data/test/session_test/activation_test.rb +3 -1
- data/test/session_test/active_record_trickery_test.rb +7 -4
- data/test/session_test/brute_force_protection_test.rb +11 -9
- data/test/session_test/callbacks_test.rb +12 -4
- data/test/session_test/cookies_test.rb +48 -5
- data/test/session_test/existence_test.rb +18 -5
- data/test/session_test/foundation_test.rb +19 -1
- data/test/session_test/http_auth_test.rb +11 -7
- data/test/session_test/id_test.rb +3 -1
- data/test/session_test/klass_test.rb +3 -1
- data/test/session_test/magic_columns_test.rb +13 -13
- data/test/session_test/magic_states_test.rb +3 -1
- data/test/session_test/params_test.rb +13 -5
- data/test/session_test/password_test.rb +10 -8
- data/test/session_test/perishability_test.rb +3 -1
- data/test/session_test/persistence_test.rb +4 -1
- data/test/session_test/scopes_test.rb +16 -8
- data/test/session_test/session_test.rb +6 -4
- data/test/session_test/timeout_test.rb +4 -2
- data/test/session_test/unauthorized_record_test.rb +4 -2
- data/test/session_test/validation_test.rb +3 -1
- data/test/test_helper.rb +84 -45
- metadata +87 -73
- data/.github/ISSUE_TEMPLATE.md +0 -13
- data/test/gemfiles/Gemfile.rails-3.2.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.0.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.1.x +0 -7
- data/test/gemfiles/Gemfile.rails-4.2.x +0 -7
- data/test/gemfiles/Gemfile.rails-5.0.x +0 -6
@@ -3,7 +3,7 @@ module Authlogic
|
|
3
3
|
# to an account, you want to make sure only users that belong to that account can
|
4
4
|
# actually login into that account. Simple, just do:
|
5
5
|
#
|
6
|
-
# class Account <
|
6
|
+
# class Account < ApplicationRecord
|
7
7
|
# authenticates_many :user_sessions
|
8
8
|
# end
|
9
9
|
#
|
@@ -17,8 +17,16 @@ module Authlogic
|
|
17
17
|
# Checkout the authenticates_many method for a list of options.
|
18
18
|
# You may also want to checkout Authlogic::ActsAsAuthentic::Scope to scope your model.
|
19
19
|
module AuthenticatesMany
|
20
|
+
# These methods become class methods of ::ActiveRecord::Base.
|
20
21
|
module Base
|
21
|
-
|
22
|
+
DPR_AUTH_MANY = <<~EOS.freeze
|
23
|
+
authenticates_many is deprecated without replacement. Let us know
|
24
|
+
if you would like to take over maintenance of this feature as a separate
|
25
|
+
gem. If no one volunteers to extract and maintain a new gem, then this
|
26
|
+
feature will simply be deleted.
|
27
|
+
EOS
|
28
|
+
|
29
|
+
# Allows you to set up a relationship with your sessions. See module
|
22
30
|
# definition above for more details.
|
23
31
|
#
|
24
32
|
# === Options
|
@@ -26,37 +34,45 @@ module Authlogic
|
|
26
34
|
# * <tt>session_class:</tt> default: "#{name}Session",
|
27
35
|
# This is the related session class.
|
28
36
|
#
|
29
|
-
# * <tt>relationship_name:</tt>
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
37
|
+
# * <tt>relationship_name:</tt>
|
38
|
+
# default: options[:session_class].klass_name.underscore.pluralize,
|
39
|
+
# This is the name of the relationship you want to use to scope
|
40
|
+
# everything. For example an Account has many Users. There should be a
|
41
|
+
# relationship called :users that you defined with a has_many. The
|
42
|
+
# reason we use the relationship is so you don't have to repeat
|
43
|
+
# yourself. The relationship could have all kinds of custom options. So
|
44
|
+
# instead of repeating yourself we essentially use the scope that the
|
35
45
|
# relationship creates.
|
36
46
|
#
|
37
47
|
# * <tt>find_options:</tt> default: nil,
|
38
|
-
# By default the find options are created from the relationship you
|
39
|
-
# :relationship_name. But if you want to override this and
|
40
|
-
# find_options you can do it here. Specify options just
|
41
|
-
# ActiveRecord::Base.find.
|
48
|
+
# By default the find options are created from the relationship you
|
49
|
+
# specify with :relationship_name. But if you want to override this and
|
50
|
+
# manually specify find_options you can do it here. Specify options just
|
51
|
+
# as you would in ActiveRecord::Base.find.
|
42
52
|
#
|
43
53
|
# * <tt>scope_cookies:</tt> default: false
|
44
|
-
# By the nature of cookies they scope themselves if you are using
|
45
|
-
# access accounts. If you aren't using subdomains you need
|
46
|
-
# cookies for each account, assuming a user is logging
|
47
|
-
# Authlogic can take care of this for you by
|
48
|
-
#
|
49
|
-
#
|
54
|
+
# By the nature of cookies they scope themselves if you are using
|
55
|
+
# subdomains to access accounts. If you aren't using subdomains you need
|
56
|
+
# to have separate cookies for each account, assuming a user is logging
|
57
|
+
# into more than one account. Authlogic can take care of this for you by
|
58
|
+
# prefixing the name of the cookie and session with the model id.
|
59
|
+
# Because it affects both cookies names and session keys, the name
|
60
|
+
# `scope_cookies` is misleading. Perhaps simply `scope` or `scoped`
|
50
61
|
# would have been better.
|
51
62
|
def authenticates_many(name, options = {})
|
63
|
+
::ActiveSupport::Deprecation.warn(DPR_AUTH_MANY)
|
52
64
|
options[:session_class] ||= name.to_s.classify.constantize
|
53
65
|
options[:relationship_name] ||= options[:session_class].klass_name.underscore.pluralize
|
54
|
-
class_eval <<-
|
66
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
55
67
|
def #{name}
|
56
68
|
find_options = #{options[:find_options].inspect} || #{options[:relationship_name]}.where(nil)
|
57
|
-
@#{name} ||= Authlogic::AuthenticatesMany::Association.new(
|
69
|
+
@#{name} ||= Authlogic::AuthenticatesMany::Association.new(
|
70
|
+
#{options[:session_class]},
|
71
|
+
find_options,
|
72
|
+
#{options[:scope_cookies] ? "self.class.model_name.name.underscore + '_' + self.send(self.class.primary_key).to_s" : 'nil'}
|
73
|
+
)
|
58
74
|
end
|
59
|
-
|
75
|
+
EOS
|
60
76
|
end
|
61
77
|
end
|
62
78
|
|
data/lib/authlogic/config.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
module Authlogic
|
3
2
|
module Config
|
3
|
+
E_USE_NORMAL_RAILS_VALIDATION = <<~EOS.freeze
|
4
|
+
This Authlogic configuration option (%s) is deprecated. Use normal
|
5
|
+
ActiveRecord validation instead. Detailed instructions:
|
6
|
+
https://github.com/binarylogic/authlogic/blob/master/doc/use_normal_rails_validation.md
|
7
|
+
EOS
|
8
|
+
|
4
9
|
def self.extended(klass)
|
5
10
|
klass.class_eval do
|
6
11
|
class_attribute :acts_as_authentic_config
|
@@ -10,15 +15,21 @@ module Authlogic
|
|
10
15
|
|
11
16
|
private
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def deprecate_authlogic_config(method_name)
|
19
|
+
::ActiveSupport::Deprecation.warn(
|
20
|
+
format(E_USE_NORMAL_RAILS_VALIDATION, method_name)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
# This is a one-liner method to write a config setting, read the config
|
25
|
+
# setting, and also set a default value for the setting.
|
26
|
+
def rw_config(key, value, default_value = nil)
|
27
|
+
if value.nil?
|
28
|
+
acts_as_authentic_config.include?(key) ? acts_as_authentic_config[key] : default_value
|
29
|
+
else
|
30
|
+
self.acts_as_authentic_config = acts_as_authentic_config.merge(key => value)
|
31
|
+
value
|
22
32
|
end
|
33
|
+
end
|
23
34
|
end
|
24
35
|
end
|
@@ -3,16 +3,19 @@ module Authlogic
|
|
3
3
|
# Allows you to use Authlogic in any framework you want, not just rails. See the RailsAdapter
|
4
4
|
# for an example of how to adapt Authlogic to work with your framework.
|
5
5
|
class AbstractAdapter
|
6
|
+
E_COOKIE_DOMAIN_ADAPTER = "The cookie_domain method has not been " \
|
7
|
+
"implemented by the controller adapter".freeze
|
8
|
+
|
6
9
|
attr_accessor :controller
|
7
10
|
|
8
11
|
def initialize(controller)
|
9
12
|
self.controller = controller
|
10
13
|
end
|
11
14
|
|
12
|
-
def authenticate_with_http_basic
|
15
|
+
def authenticate_with_http_basic
|
13
16
|
@auth = Rack::Auth::Basic::Request.new(controller.request.env)
|
14
|
-
if @auth.provided?
|
15
|
-
|
17
|
+
if @auth.provided? && @auth.basic?
|
18
|
+
yield(*@auth.credentials)
|
16
19
|
else
|
17
20
|
false
|
18
21
|
end
|
@@ -23,7 +26,7 @@ module Authlogic
|
|
23
26
|
end
|
24
27
|
|
25
28
|
def cookie_domain
|
26
|
-
raise NotImplementedError.new(
|
29
|
+
raise NotImplementedError.new(E_COOKIE_DOMAIN_ADAPTER)
|
27
30
|
end
|
28
31
|
|
29
32
|
def params
|
@@ -50,19 +53,43 @@ module Authlogic
|
|
50
53
|
controller.send(:single_access_allowed?)
|
51
54
|
end
|
52
55
|
|
53
|
-
|
54
|
-
|
56
|
+
# You can disable the updating of `last_request_at`
|
57
|
+
# on a per-controller basis.
|
58
|
+
#
|
59
|
+
# # in your controller
|
60
|
+
# def last_request_update_allowed?
|
61
|
+
# false
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# For example, what if you had a javascript function that polled the
|
65
|
+
# server updating how much time is left in their session before it
|
66
|
+
# times out. Obviously you would want to ignore this request, because
|
67
|
+
# then the user would never time out. So you can do something like
|
68
|
+
# this in your controller:
|
69
|
+
#
|
70
|
+
# def last_request_update_allowed?
|
71
|
+
# action_name != "update_session_time_left"
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# See `authlogic/session/magic_columns.rb` to learn more about the
|
75
|
+
# `last_request_at` column itself.
|
76
|
+
def last_request_update_allowed?
|
77
|
+
if controller.respond_to?(:last_request_update_allowed?, true)
|
78
|
+
controller.send(:last_request_update_allowed?)
|
79
|
+
else
|
80
|
+
true
|
81
|
+
end
|
55
82
|
end
|
56
83
|
|
57
|
-
def
|
58
|
-
controller.
|
84
|
+
def respond_to_missing?(*args)
|
85
|
+
super(*args) || controller.respond_to?(*args)
|
59
86
|
end
|
60
87
|
|
61
88
|
private
|
62
89
|
|
63
|
-
|
64
|
-
|
65
|
-
|
90
|
+
def method_missing(id, *args, &block)
|
91
|
+
controller.send(id, *args, &block)
|
92
|
+
end
|
66
93
|
end
|
67
94
|
end
|
68
95
|
end
|
@@ -32,7 +32,7 @@ module Authlogic
|
|
32
32
|
# # Authlogic options go here
|
33
33
|
# end
|
34
34
|
#
|
35
|
-
# class User <
|
35
|
+
# class User < ApplicationRecord
|
36
36
|
# acts_as_authentic
|
37
37
|
# end
|
38
38
|
#
|
@@ -48,7 +48,7 @@ module Authlogic
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def remote_ip
|
51
|
-
|
51
|
+
ip
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -56,10 +56,14 @@ module Authlogic
|
|
56
56
|
Authlogic::Session::Base.controller = self
|
57
57
|
end
|
58
58
|
|
59
|
-
# Rack Requests stores cookies with not just the value, but also with
|
60
|
-
#
|
59
|
+
# Rack Requests stores cookies with not just the value, but also with
|
60
|
+
# flags and expire information in the hash. Authlogic does not like this,
|
61
|
+
# so we drop everything except the cookie value.
|
61
62
|
def cookies
|
62
|
-
controller
|
63
|
+
controller
|
64
|
+
.cookies
|
65
|
+
.map { |key, value_hash| { key => value_hash[:value] } }
|
66
|
+
.inject(:merge) || {}
|
63
67
|
end
|
64
68
|
end
|
65
69
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "action_controller"
|
2
2
|
|
3
3
|
module Authlogic
|
4
4
|
module ControllerAdapters
|
@@ -13,12 +13,14 @@ module Authlogic
|
|
13
13
|
controller.authenticate_with_http_basic(&block)
|
14
14
|
end
|
15
15
|
|
16
|
+
# Returns a `ActionDispatch::Cookies::CookieJar`. See the AC guide
|
17
|
+
# http://guides.rubyonrails.org/action_controller_overview.html#cookies
|
16
18
|
def cookies
|
17
19
|
controller.send(:cookies)
|
18
20
|
end
|
19
21
|
|
20
22
|
def cookie_domain
|
21
|
-
@cookie_domain_key ||= Rails::VERSION::STRING >=
|
23
|
+
@cookie_domain_key ||= Rails::VERSION::STRING >= "2.3" ? :domain : :session_domain
|
22
24
|
controller.request.session_options[@cookie_domain_key]
|
23
25
|
end
|
24
26
|
|
@@ -32,7 +34,7 @@ module Authlogic
|
|
32
34
|
def self.included(klass) # :nodoc:
|
33
35
|
if defined?(::ApplicationController)
|
34
36
|
raise AuthlogicLoadedTooLateError.new(
|
35
|
-
|
37
|
+
<<~EOS.squish
|
36
38
|
Authlogic is trying to add a callback to ActionController::Base
|
37
39
|
but ApplicationController has already been loaded, so the
|
38
40
|
callback won't be copied into your application. Generally this
|
@@ -54,12 +56,15 @@ module Authlogic
|
|
54
56
|
|
55
57
|
private
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
-
|
59
|
+
def activate_authlogic
|
60
|
+
Authlogic::Session::Base.controller = RailsAdapter.new(self)
|
61
|
+
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
65
|
-
ActionController::Base.send(
|
67
|
+
ActionController::Base.send(
|
68
|
+
:include,
|
69
|
+
Authlogic::ControllerAdapters::RailsAdapter::RailsImplementation
|
70
|
+
)
|
@@ -32,7 +32,7 @@ module Authlogic
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def session
|
35
|
-
env[
|
35
|
+
env["rack.session"]
|
36
36
|
end
|
37
37
|
|
38
38
|
def method_missing(meth, *args, &block)
|
@@ -42,7 +42,7 @@ module Authlogic
|
|
42
42
|
|
43
43
|
class Adapter < AbstractAdapter
|
44
44
|
def cookie_domain
|
45
|
-
env[
|
45
|
+
env["SERVER_NAME"]
|
46
46
|
end
|
47
47
|
|
48
48
|
module Implementation
|
@@ -2,21 +2,23 @@ require "openssl"
|
|
2
2
|
|
3
3
|
module Authlogic
|
4
4
|
module CryptoProviders
|
5
|
-
# This encryption method is reversible if you have the supplied key. So in
|
6
|
-
# use this encryption method you must supply it with a key first.
|
7
|
-
# or before your application initializes, you should do
|
5
|
+
# This encryption method is reversible if you have the supplied key. So in
|
6
|
+
# order to use this encryption method you must supply it with a key first.
|
7
|
+
# In an initializer, or before your application initializes, you should do
|
8
|
+
# the following:
|
8
9
|
#
|
9
|
-
# Authlogic::CryptoProviders::AES256.key = "
|
10
|
+
# Authlogic::CryptoProviders::AES256.key = "long, unique, and random key"
|
10
11
|
#
|
11
|
-
# My final comment is that this is a strong encryption method, but its main
|
12
|
-
# is that it's reversible. If you do not need to reverse the hash
|
13
|
-
# consider Sha512 or BCrypt instead.
|
12
|
+
# My final comment is that this is a strong encryption method, but its main
|
13
|
+
# weakness is that it's reversible. If you do not need to reverse the hash
|
14
|
+
# then you should consider Sha512 or BCrypt instead.
|
14
15
|
#
|
15
|
-
# Keep your key in a safe place, some even say the key should be stored on a
|
16
|
-
# server. This won't hurt performance because the only time it will
|
17
|
-
# key on the separate server is during initialization,
|
18
|
-
# reasoning behind this is if someone does
|
19
|
-
#
|
16
|
+
# Keep your key in a safe place, some even say the key should be stored on a
|
17
|
+
# separate server. This won't hurt performance because the only time it will
|
18
|
+
# try and access the key on the separate server is during initialization,
|
19
|
+
# which only happens once. The reasoning behind this is if someone does
|
20
|
+
# compromise your server they won't have the key also. Basically, you don't
|
21
|
+
# want to store the key with the lock.
|
20
22
|
class AES256
|
21
23
|
class << self
|
22
24
|
attr_writer :key
|
@@ -37,29 +39,32 @@ module Authlogic
|
|
37
39
|
|
38
40
|
private
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
@aes ||= openssl_cipher_class.new("AES-256-ECB")
|
42
|
+
def aes
|
43
|
+
if @key.blank?
|
44
|
+
raise ArgumentError.new(
|
45
|
+
"You must provide a key like #{name}.key = my_key before using the #{name}"
|
46
|
+
)
|
48
47
|
end
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
49
|
+
@aes ||= openssl_cipher_class.new("AES-256-ECB")
|
50
|
+
end
|
51
|
+
|
52
|
+
# `::OpenSSL::Cipher::Cipher` has been deprecated since at least 2014,
|
53
|
+
# in favor of `::OpenSSL::Cipher`, but a deprecation warning was not
|
54
|
+
# printed until 2016
|
55
|
+
# (https://github.com/ruby/openssl/commit/5c20a4c014) when openssl
|
56
|
+
# became a gem. Its first release as a gem was 2.0.0, in ruby 2.4.
|
57
|
+
# (See https://github.com/ruby/ruby/blob/v2_4_0/NEWS)
|
58
|
+
#
|
59
|
+
# When we eventually drop support for ruby < 2.4, we can probably also
|
60
|
+
# drop support for openssl gem < 2.
|
61
|
+
def openssl_cipher_class
|
62
|
+
if ::Gem::Version.new(::OpenSSL::VERSION) < ::Gem::Version.new("2.0.0")
|
63
|
+
::OpenSSL::Cipher::Cipher
|
64
|
+
else
|
65
|
+
::OpenSSL::Cipher
|
62
66
|
end
|
67
|
+
end
|
63
68
|
end
|
64
69
|
end
|
65
70
|
end
|
@@ -16,10 +16,18 @@ module Authlogic
|
|
16
16
|
# require "benchmark"
|
17
17
|
#
|
18
18
|
# Benchmark.bm(18) do |x|
|
19
|
-
# x.report("BCrypt (cost = 10:") {
|
20
|
-
#
|
21
|
-
#
|
22
|
-
# x.report("
|
19
|
+
# x.report("BCrypt (cost = 10:") {
|
20
|
+
# 100.times { BCrypt::Password.create("mypass", :cost => 10) }
|
21
|
+
# }
|
22
|
+
# x.report("BCrypt (cost = 4:") {
|
23
|
+
# 100.times { BCrypt::Password.create("mypass", :cost => 4) }
|
24
|
+
# }
|
25
|
+
# x.report("Sha512:") {
|
26
|
+
# 100.times { Digest::SHA512.hexdigest("mypass") }
|
27
|
+
# }
|
28
|
+
# x.report("Sha1:") {
|
29
|
+
# 100.times { Digest::SHA1.hexdigest("mypass") }
|
30
|
+
# }
|
23
31
|
# end
|
24
32
|
#
|
25
33
|
# user system total real
|
@@ -66,7 +74,7 @@ module Authlogic
|
|
66
74
|
|
67
75
|
# Creates a BCrypt hash for the password passed.
|
68
76
|
def encrypt(*tokens)
|
69
|
-
::BCrypt::Password.create(join_tokens(tokens), :
|
77
|
+
::BCrypt::Password.create(join_tokens(tokens), cost: cost)
|
70
78
|
end
|
71
79
|
|
72
80
|
# Does the hash match the tokens? Uses the same tokens that were used to
|
@@ -90,17 +98,15 @@ module Authlogic
|
|
90
98
|
|
91
99
|
private
|
92
100
|
|
93
|
-
|
94
|
-
|
95
|
-
|
101
|
+
def join_tokens(tokens)
|
102
|
+
tokens.flatten.join
|
103
|
+
end
|
96
104
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
end
|
103
|
-
end
|
105
|
+
def new_from_hash(hash)
|
106
|
+
::BCrypt::Password.new(hash)
|
107
|
+
rescue ::BCrypt::Errors::InvalidHash
|
108
|
+
nil
|
109
|
+
end
|
104
110
|
end
|
105
111
|
end
|
106
112
|
end
|
@@ -6,7 +6,8 @@ module Authlogic
|
|
6
6
|
# I highly discourage using this crypto provider as it superbly inferior
|
7
7
|
# to your other options.
|
8
8
|
#
|
9
|
-
# Please use any other provider offered by Authlogic
|
9
|
+
# Please use any other provider offered by Authlogic (except AES256, that
|
10
|
+
# would be even worse).
|
10
11
|
class MD5
|
11
12
|
class << self
|
12
13
|
attr_accessor :join_token
|
@@ -24,7 +25,8 @@ module Authlogic
|
|
24
25
|
digest
|
25
26
|
end
|
26
27
|
|
27
|
-
# Does the crypted password match the tokens? Uses the same tokens that
|
28
|
+
# Does the crypted password match the tokens? Uses the same tokens that
|
29
|
+
# were used to encrypt.
|
28
30
|
def matches?(crypted, *tokens)
|
29
31
|
encrypt(*tokens) == crypted
|
30
32
|
end
|
@@ -19,7 +19,13 @@ module Authlogic
|
|
19
19
|
# end
|
20
20
|
class SCrypt
|
21
21
|
class << self
|
22
|
-
DEFAULTS = {
|
22
|
+
DEFAULTS = {
|
23
|
+
key_len: 32,
|
24
|
+
salt_size: 8,
|
25
|
+
max_time: 0.2,
|
26
|
+
max_mem: 1024 * 1024,
|
27
|
+
max_memfrac: 0.5
|
28
|
+
}.freeze
|
23
29
|
|
24
30
|
attr_writer :key_len, :salt_size, :max_time, :max_mem, :max_memfrac
|
25
31
|
# Key length - length in bytes of generated key, from 16 to 512.
|
@@ -42,7 +48,8 @@ module Authlogic
|
|
42
48
|
@max_mem ||= DEFAULTS[:max_mem]
|
43
49
|
end
|
44
50
|
|
45
|
-
# Max memory fraction - maximum memory out of all available. Always
|
51
|
+
# Max memory fraction - maximum memory out of all available. Always
|
52
|
+
# greater than zero and <= 0.5.
|
46
53
|
def max_memfrac
|
47
54
|
@max_memfrac ||= DEFAULTS[:max_memfrac]
|
48
55
|
end
|
@@ -51,11 +58,11 @@ module Authlogic
|
|
51
58
|
def encrypt(*tokens)
|
52
59
|
::SCrypt::Password.create(
|
53
60
|
join_tokens(tokens),
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
61
|
+
key_len: key_len,
|
62
|
+
salt_size: salt_size,
|
63
|
+
max_mem: max_mem,
|
64
|
+
max_memfrac: max_memfrac,
|
65
|
+
max_time: max_time
|
59
66
|
)
|
60
67
|
end
|
61
68
|
|
@@ -68,17 +75,15 @@ module Authlogic
|
|
68
75
|
|
69
76
|
private
|
70
77
|
|
71
|
-
|
72
|
-
|
73
|
-
|
78
|
+
def join_tokens(tokens)
|
79
|
+
tokens.flatten.join
|
80
|
+
end
|
74
81
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
81
|
-
end
|
82
|
+
def new_from_hash(hash)
|
83
|
+
::SCrypt::Password.new(hash)
|
84
|
+
rescue ::SCrypt::Errors::InvalidHash
|
85
|
+
nil
|
86
|
+
end
|
82
87
|
end
|
83
88
|
end
|
84
89
|
end
|
@@ -2,8 +2,10 @@ require "digest/sha1"
|
|
2
2
|
|
3
3
|
module Authlogic
|
4
4
|
module CryptoProviders
|
5
|
-
# This class was made for the users transitioning from
|
6
|
-
#
|
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
9
|
class Sha1
|
8
10
|
class << self
|
9
11
|
def join_token
|
@@ -11,7 +13,8 @@ module Authlogic
|
|
11
13
|
end
|
12
14
|
attr_writer :join_token
|
13
15
|
|
14
|
-
# The number of times to loop through the encryption. This is ten
|
16
|
+
# The number of times to loop through the encryption. This is ten
|
17
|
+
# because that is what restful_authentication defaults to.
|
15
18
|
def stretches
|
16
19
|
@stretches ||= 10
|
17
20
|
end
|
@@ -21,11 +24,14 @@ module Authlogic
|
|
21
24
|
def encrypt(*tokens)
|
22
25
|
tokens = tokens.flatten
|
23
26
|
digest = tokens.shift
|
24
|
-
stretches.times
|
27
|
+
stretches.times do
|
28
|
+
digest = Digest::SHA1.hexdigest([digest, *tokens].join(join_token))
|
29
|
+
end
|
25
30
|
digest
|
26
31
|
end
|
27
32
|
|
28
|
-
# Does the crypted password match the tokens? Uses the same tokens that
|
33
|
+
# Does the crypted password match the tokens? Uses the same tokens that
|
34
|
+
# were used to encrypt.
|
29
35
|
def matches?(crypted, *tokens)
|
30
36
|
encrypt(*tokens) == crypted
|
31
37
|
end
|
@@ -1,22 +1,25 @@
|
|
1
1
|
require "digest/sha2"
|
2
2
|
|
3
3
|
module Authlogic
|
4
|
-
# The acts_as_authentic method has a crypto_provider option. This allows you
|
5
|
-
#
|
4
|
+
# The acts_as_authentic method has a crypto_provider option. This allows you
|
5
|
+
# to use any type of encryption you like. Just create a class with a class
|
6
|
+
# level encrypt and matches? method. See example below.
|
6
7
|
#
|
7
8
|
# === Example
|
8
9
|
#
|
9
10
|
# class MyAwesomeEncryptionMethod
|
10
11
|
# def self.encrypt(*tokens)
|
11
|
-
# # the tokens passed will be an array of objects, what type of object
|
12
|
-
# # just do what you need to do with them and return a
|
13
|
-
# # for example, you will most likely join all
|
12
|
+
# # the tokens passed will be an array of objects, what type of object
|
13
|
+
# # is irrelevant, just do what you need to do with them and return a
|
14
|
+
# # single encrypted string. for example, you will most likely join all
|
15
|
+
# # of the objects into a single string and then encrypt that string
|
14
16
|
# end
|
15
17
|
#
|
16
18
|
# def self.matches?(crypted, *tokens)
|
17
|
-
# # return true if the crypted string matches the tokens.
|
18
|
-
# #
|
19
|
-
# # encrypt the tokens and make sure it matches the
|
19
|
+
# # return true if the crypted string matches the tokens. Depending on
|
20
|
+
# # your algorithm you might decrypt the string then compare it to the
|
21
|
+
# # token, or you might encrypt the tokens and make sure it matches the
|
22
|
+
# # crypted string, its up to you.
|
20
23
|
# end
|
21
24
|
# end
|
22
25
|
module CryptoProviders
|
@@ -40,7 +43,8 @@ module Authlogic
|
|
40
43
|
digest
|
41
44
|
end
|
42
45
|
|
43
|
-
# Does the crypted password match the tokens? Uses the same tokens that
|
46
|
+
# Does the crypted password match the tokens? Uses the same tokens that
|
47
|
+
# were used to encrypt.
|
44
48
|
def matches?(crypted, *tokens)
|
45
49
|
encrypt(*tokens) == crypted
|
46
50
|
end
|