authlogic 3.8.0 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|