authlogic 3.5.0 → 3.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.github/ISSUE_TEMPLATE.md +13 -0
- data/.rubocop_todo.yml +1 -37
- data/.travis.yml +15 -6
- data/CHANGELOG.md +2 -130
- data/CONTRIBUTING.md +13 -2
- data/README.md +2 -3
- data/authlogic.gemspec +4 -5
- data/lib/authlogic/acts_as_authentic/base.rb +4 -2
- data/lib/authlogic/acts_as_authentic/email.rb +8 -3
- data/lib/authlogic/acts_as_authentic/logged_in_status.rb +21 -3
- data/lib/authlogic/acts_as_authentic/login.rb +44 -25
- data/lib/authlogic/acts_as_authentic/password.rb +28 -12
- data/lib/authlogic/acts_as_authentic/perishable_token.rb +21 -12
- data/lib/authlogic/acts_as_authentic/restful_authentication.rb +16 -9
- data/lib/authlogic/acts_as_authentic/session_maintenance.rb +5 -3
- data/lib/authlogic/authenticates_many/association.rb +11 -4
- data/lib/authlogic/authenticates_many/base.rb +5 -4
- data/lib/authlogic/controller_adapters/rack_adapter.rb +6 -2
- data/lib/authlogic/controller_adapters/rails_adapter.rb +11 -8
- data/lib/authlogic/crypto_providers/aes256.rb +21 -2
- data/lib/authlogic/crypto_providers/bcrypt.rb +4 -1
- data/lib/authlogic/crypto_providers/sha512.rb +15 -10
- data/lib/authlogic/regex.rb +7 -6
- data/lib/authlogic/session/activation.rb +19 -10
- data/lib/authlogic/session/cookies.rb +3 -1
- data/lib/authlogic/session/id.rb +13 -7
- data/lib/authlogic/session/magic_columns.rb +19 -10
- data/lib/authlogic/session/magic_states.rb +7 -1
- data/lib/authlogic/session/password.rb +82 -35
- data/lib/authlogic/session/perishable_token.rb +7 -3
- data/lib/authlogic/session/validation.rb +13 -11
- data/lib/authlogic/test_case.rb +52 -32
- data/lib/authlogic.rb +6 -0
- data/test/acts_as_authentic_test/email_test.rb +33 -27
- data/test/acts_as_authentic_test/logged_in_status_test.rb +2 -2
- data/test/acts_as_authentic_test/login_test.rb +50 -37
- data/test/acts_as_authentic_test/magic_columns_test.rb +8 -8
- data/test/acts_as_authentic_test/password_test.rb +14 -14
- data/test/acts_as_authentic_test/perishable_token_test.rb +5 -5
- data/test/acts_as_authentic_test/persistence_token_test.rb +4 -4
- data/test/acts_as_authentic_test/restful_authentication_test.rb +6 -6
- data/test/acts_as_authentic_test/session_maintenance_test.rb +15 -10
- data/test/acts_as_authentic_test/single_access_test.rb +6 -6
- data/test/authenticates_many_test.rb +21 -6
- data/test/gemfiles/Gemfile.rails-5.1.x +6 -0
- data/test/gemfiles/Gemfile.rails-5.2.x +6 -0
- data/test/libs/company.rb +1 -1
- data/test/session_test/activation_test.rb +1 -1
- data/test/session_test/active_record_trickery_test.rb +3 -3
- data/test/session_test/brute_force_protection_test.rb +19 -14
- data/test/session_test/cookies_test.rb +21 -12
- data/test/session_test/existence_test.rb +15 -10
- data/test/session_test/http_auth_test.rb +2 -2
- data/test/session_test/magic_columns_test.rb +7 -4
- data/test/session_test/magic_states_test.rb +7 -9
- data/test/session_test/params_test.rb +6 -6
- data/test/session_test/password_test.rb +2 -2
- data/test/session_test/perishability_test.rb +1 -1
- data/test/session_test/persistence_test.rb +2 -2
- data/test/session_test/timeout_test.rb +7 -5
- data/test/session_test/validation_test.rb +1 -1
- data/test/test_helper.rb +37 -6
- metadata +57 -36
- checksums.yaml +0 -7
@@ -16,16 +16,19 @@ module Authlogic
|
|
16
16
|
|
17
17
|
# Password configuration
|
18
18
|
module Config
|
19
|
-
# Authlogic tries to validate the credentials passed to it. One part of
|
20
|
-
#
|
19
|
+
# Authlogic tries to validate the credentials passed to it. One part of
|
20
|
+
# validation is actually finding the user and making sure it exists.
|
21
|
+
# What method it uses the do this is up to you.
|
21
22
|
#
|
22
|
-
# Let's say you have a UserSession that is authenticating a User. By
|
23
|
-
#
|
24
|
-
#
|
23
|
+
# Let's say you have a UserSession that is authenticating a User. By
|
24
|
+
# default UserSession will call User.find_by_login(login). You can
|
25
|
+
# change what method UserSession calls by specifying it here. Then in
|
26
|
+
# your User model you can make that method do anything you want, giving
|
27
|
+
# you complete control of how users are found by the UserSession.
|
25
28
|
#
|
26
|
-
# Let's take an example: You want to allow users to login by username or
|
27
|
-
# Set this to the name of the class method that does this in the
|
28
|
-
# call it "find_by_username_or_email"
|
29
|
+
# Let's take an example: You want to allow users to login by username or
|
30
|
+
# email. Set this to the name of the class method that does this in the
|
31
|
+
# User model. Let's call it "find_by_username_or_email"
|
29
32
|
#
|
30
33
|
# class User < ActiveRecord::Base
|
31
34
|
# def self.find_by_username_or_email(login)
|
@@ -33,10 +36,10 @@ module Authlogic
|
|
33
36
|
# end
|
34
37
|
# end
|
35
38
|
#
|
36
|
-
# Now just specify the name of this method for this configuration option
|
37
|
-
# are all set. You can do anything you want here. Maybe you
|
38
|
-
# multiple logins and you want to search a has_many
|
39
|
-
# the limit.
|
39
|
+
# Now just specify the name of this method for this configuration option
|
40
|
+
# and you are all set. You can do anything you want here. Maybe you
|
41
|
+
# allow users to have multiple logins and you want to search a has_many
|
42
|
+
# relationship, etc. The sky is the limit.
|
40
43
|
#
|
41
44
|
# * <tt>Default:</tt> "find_by_smart_case_login_field"
|
42
45
|
# * <tt>Accepts:</tt> Symbol or String
|
@@ -45,10 +48,11 @@ module Authlogic
|
|
45
48
|
end
|
46
49
|
alias_method :find_by_login_method=, :find_by_login_method
|
47
50
|
|
48
|
-
# The text used to identify credentials (username/password) combination
|
49
|
-
# login attempt occurs. When you show error messages for a
|
50
|
-
# considered good security practice to hide which field
|
51
|
-
# incorrectly (the login field or the password
|
51
|
+
# The text used to identify credentials (username/password) combination
|
52
|
+
# when a bad login attempt occurs. When you show error messages for a
|
53
|
+
# bad login, it's considered good security practice to hide which field
|
54
|
+
# the user has entered incorrectly (the login field or the password
|
55
|
+
# field). For a full explanation, see
|
52
56
|
# http://www.gnucitizen.org/blog/username-enumeration-vulnerabilities/
|
53
57
|
#
|
54
58
|
# Example of use:
|
@@ -57,7 +61,8 @@ module Authlogic
|
|
57
61
|
# generalize_credentials_error_messages true
|
58
62
|
# end
|
59
63
|
#
|
60
|
-
# This would make the error message for bad logins and bad passwords
|
64
|
+
# This would make the error message for bad logins and bad passwords
|
65
|
+
# look identical:
|
61
66
|
#
|
62
67
|
# Login/Password combination is not valid
|
63
68
|
#
|
@@ -69,12 +74,14 @@ module Authlogic
|
|
69
74
|
#
|
70
75
|
# This will instead show your custom error message when the UserSession is invalid.
|
71
76
|
#
|
72
|
-
# The downside to enabling this is that is can be too vague for a user
|
73
|
-
#
|
77
|
+
# The downside to enabling this is that is can be too vague for a user
|
78
|
+
# that has a hard time remembering their username and password
|
79
|
+
# combinations. It also disables the ability to to highlight the field
|
74
80
|
# with the error when you use form_for.
|
75
81
|
#
|
76
|
-
# If you are developing an app where security is an extreme priority
|
77
|
-
#
|
82
|
+
# If you are developing an app where security is an extreme priority
|
83
|
+
# (such as a financial application), then you should enable this.
|
84
|
+
# Otherwise, leaving this off is fine.
|
78
85
|
#
|
79
86
|
# * <tt>Default</tt> false
|
80
87
|
# * <tt>Accepts:</tt> Boolean
|
@@ -83,12 +90,13 @@ module Authlogic
|
|
83
90
|
end
|
84
91
|
alias_method :generalize_credentials_error_messages=, :generalize_credentials_error_messages
|
85
92
|
|
86
|
-
# The name of the method you want Authlogic to create for storing the
|
87
|
-
# username. Keep in mind this is just for your
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
93
|
+
# The name of the method you want Authlogic to create for storing the
|
94
|
+
# login / username. Keep in mind this is just for your
|
95
|
+
# Authlogic::Session, if you want it can be something completely
|
96
|
+
# different than the field in your model. So if you wanted people to
|
97
|
+
# login with a field called "login" and then find users by email this is
|
98
|
+
# completely doable. See the find_by_login_method configuration option
|
99
|
+
# for more details.
|
92
100
|
#
|
93
101
|
# * <tt>Default:</tt> klass.login_field || klass.email_field
|
94
102
|
# * <tt>Accepts:</tt> Symbol or String
|
@@ -97,7 +105,8 @@ module Authlogic
|
|
97
105
|
end
|
98
106
|
alias_method :login_field=, :login_field
|
99
107
|
|
100
|
-
# Works exactly like login_field, but for the password instead. Returns
|
108
|
+
# Works exactly like login_field, but for the password instead. Returns
|
109
|
+
# :password if a login_field exists.
|
101
110
|
#
|
102
111
|
# * <tt>Default:</tt> :password
|
103
112
|
# * <tt>Accepts:</tt> Symbol or String
|
@@ -106,8 +115,9 @@ module Authlogic
|
|
106
115
|
end
|
107
116
|
alias_method :password_field=, :password_field
|
108
117
|
|
109
|
-
# The name of the method in your model used to verify the password. This
|
110
|
-
# be
|
118
|
+
# The name of the method in your model used to verify the password. This
|
119
|
+
# should be an instance method. It should also be prepared to accept a
|
120
|
+
# raw password and a crytped password.
|
111
121
|
#
|
112
122
|
# * <tt>Default:</tt> "valid_password?"
|
113
123
|
# * <tt>Accepts:</tt> Symbol or String
|
@@ -117,8 +127,40 @@ module Authlogic
|
|
117
127
|
alias_method :verify_password_method=, :verify_password_method
|
118
128
|
end
|
119
129
|
|
120
|
-
# Password
|
130
|
+
# Password-related instance methods
|
121
131
|
module InstanceMethods
|
132
|
+
E_AC_PARAMETERS = <<-STR.strip_heredoc.freeze
|
133
|
+
You have passed an ActionController::Parameters to Authlogic 3. That's
|
134
|
+
OK for now, but in Authlogic 4, it will raise an error. Please
|
135
|
+
replace:
|
136
|
+
|
137
|
+
UserSession.new(user_session_params)
|
138
|
+
UserSession.create(user_session_params)
|
139
|
+
|
140
|
+
with
|
141
|
+
|
142
|
+
UserSession.new(user_session_params.to_h)
|
143
|
+
UserSession.create(user_session_params.to_h)
|
144
|
+
|
145
|
+
And don't forget to `permit`!
|
146
|
+
|
147
|
+
During the transition of rails to Strong Parameters, it has been
|
148
|
+
common for Authlogic users to forget to `permit` their params. They
|
149
|
+
would pass their params into Authlogic, we'd call `to_h`, and they'd
|
150
|
+
be surprised when authentication failed.
|
151
|
+
|
152
|
+
In 2018, people are still making this mistake. We'd like to help them
|
153
|
+
and make authlogic a little simpler at the same time, so in Authlogic
|
154
|
+
3.7.0, we deprecated the use of ActionController::Parameters.
|
155
|
+
|
156
|
+
We discussed this issue thoroughly between late 2016 and early
|
157
|
+
2018. Notable discussions include:
|
158
|
+
|
159
|
+
- https://github.com/binarylogic/authlogic/issues/512
|
160
|
+
- https://github.com/binarylogic/authlogic/pull/558
|
161
|
+
- https://github.com/binarylogic/authlogic/pull/577
|
162
|
+
STR
|
163
|
+
|
122
164
|
def initialize(*args)
|
123
165
|
if !self.class.configured_password_methods
|
124
166
|
configure_password_methods
|
@@ -127,7 +169,8 @@ module Authlogic
|
|
127
169
|
super
|
128
170
|
end
|
129
171
|
|
130
|
-
# Returns the login_field / password_field credentials combination in
|
172
|
+
# Returns the login_field / password_field credentials combination in
|
173
|
+
# hash form.
|
131
174
|
def credentials
|
132
175
|
if authenticating_with_password?
|
133
176
|
details = {}
|
@@ -139,7 +182,8 @@ module Authlogic
|
|
139
182
|
end
|
140
183
|
end
|
141
184
|
|
142
|
-
# Accepts the login_field / password_field credentials combination in
|
185
|
+
# Accepts the login_field / password_field credentials combination in
|
186
|
+
# hash form.
|
143
187
|
def credentials=(value)
|
144
188
|
super
|
145
189
|
values = parse_param_val(value) # add strong parameters check
|
@@ -168,10 +212,12 @@ module Authlogic
|
|
168
212
|
self.class.send(:attr_writer, password_field) if !respond_to?("#{password_field}=")
|
169
213
|
self.class.send(:define_method, password_field) {} if !respond_to?(password_field)
|
170
214
|
|
215
|
+
# The password should not be accessible publicly. This way forms
|
216
|
+
# using form_for don't fill the password with the attempted
|
217
|
+
# password. To prevent this we just create this method that is
|
218
|
+
# private.
|
171
219
|
self.class.class_eval <<-"end_eval", __FILE__, __LINE__
|
172
220
|
private
|
173
|
-
# The password should not be accessible publicly. This way forms using form_for don't fill the password with the
|
174
|
-
# attempted password. To prevent this we just create this method that is private.
|
175
221
|
def protected_#{password_field}
|
176
222
|
@#{password_field}
|
177
223
|
end
|
@@ -250,6 +296,7 @@ module Authlogic
|
|
250
296
|
# This method converts the ActionController::Parameters to a Hash
|
251
297
|
def parse_param_val(value)
|
252
298
|
if value.first.class.name == "ActionController::Parameters"
|
299
|
+
ActiveSupport::Deprecation.warn(E_AC_PARAMETERS)
|
253
300
|
[value.first.to_h]
|
254
301
|
else
|
255
302
|
value.is_a?(Array) ? value : [value]
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module Authlogic
|
2
2
|
module Session
|
3
|
-
# Maintains the perishable token, which is helpful for confirming records or
|
4
|
-
#
|
3
|
+
# Maintains the perishable token, which is helpful for confirming records or
|
4
|
+
# authorizing records to reset their password. All that this module does is
|
5
|
+
# reset it after a session have been saved, just keep it changing. The more
|
6
|
+
# it changes, the tighter the security.
|
5
7
|
#
|
6
8
|
# See Authlogic::ActsAsAuthentic::PerishableToken for more information.
|
7
9
|
module PerishableToken
|
@@ -12,7 +14,9 @@ module Authlogic
|
|
12
14
|
private
|
13
15
|
|
14
16
|
def reset_perishable_token!
|
15
|
-
|
17
|
+
if record.respond_to?(:reset_perishable_token) && !record.disable_perishable_token_maintenance?
|
18
|
+
record.reset_perishable_token
|
19
|
+
end
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
@@ -2,7 +2,8 @@ module Authlogic
|
|
2
2
|
module Session
|
3
3
|
# Responsible for session validation
|
4
4
|
module Validation
|
5
|
-
# The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses
|
5
|
+
# The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses
|
6
|
+
# the exact same ActiveRecord errors class. Use it the same way:
|
6
7
|
#
|
7
8
|
# class UserSession
|
8
9
|
# validate :check_if_awesome
|
@@ -22,9 +23,10 @@ module Authlogic
|
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
# You should use this as a place holder for any records that you find
|
26
|
-
#
|
27
|
-
# the
|
26
|
+
# You should use this as a place holder for any records that you find
|
27
|
+
# during validation. The main reason for this is to allow other modules to
|
28
|
+
# use it if needed. Take the failed_login_count feature, it needs this in
|
29
|
+
# order to increase the failed login count.
|
28
30
|
def attempted_record
|
29
31
|
@attempted_record
|
30
32
|
end
|
@@ -34,8 +36,8 @@ module Authlogic
|
|
34
36
|
@attempted_record = value
|
35
37
|
end
|
36
38
|
|
37
|
-
# The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses
|
38
|
-
# Use it the same way:
|
39
|
+
# The errors in Authlogic work JUST LIKE ActiveRecord. In fact, it uses
|
40
|
+
# the exact same ActiveRecord errors class. Use it the same way:
|
39
41
|
#
|
40
42
|
# === Example
|
41
43
|
#
|
@@ -52,9 +54,9 @@ module Authlogic
|
|
52
54
|
@errors ||= Errors.new(self)
|
53
55
|
end
|
54
56
|
|
55
|
-
# Determines if the information you provided for authentication is valid
|
56
|
-
# a problem with the information provided errors will
|
57
|
-
# method will return false.
|
57
|
+
# Determines if the information you provided for authentication is valid
|
58
|
+
# or not. If there is a problem with the information provided errors will
|
59
|
+
# be added to the errors object and this method will return false.
|
58
60
|
def valid?
|
59
61
|
errors.clear
|
60
62
|
self.attempted_record = nil
|
@@ -64,13 +66,13 @@ module Authlogic
|
|
64
66
|
validate
|
65
67
|
ensure_authentication_attempted
|
66
68
|
|
67
|
-
if errors.
|
69
|
+
if errors.empty?
|
68
70
|
new_session? ? after_validation_on_create : after_validation_on_update
|
69
71
|
after_validation
|
70
72
|
end
|
71
73
|
|
72
74
|
save_record(attempted_record)
|
73
|
-
errors.
|
75
|
+
errors.empty?
|
74
76
|
end
|
75
77
|
|
76
78
|
private
|
data/lib/authlogic/test_case.rb
CHANGED
@@ -5,8 +5,9 @@ require File.dirname(__FILE__) + "/test_case/mock_logger"
|
|
5
5
|
require File.dirname(__FILE__) + "/test_case/mock_request"
|
6
6
|
|
7
7
|
module Authlogic
|
8
|
-
# This module is a collection of methods and classes that help you easily test
|
9
|
-
# I use these same tools to test the internals of
|
8
|
+
# This module is a collection of methods and classes that help you easily test
|
9
|
+
# Authlogic. In fact, I use these same tools to test the internals of
|
10
|
+
# Authlogic.
|
10
11
|
#
|
11
12
|
# === The quick and dirty
|
12
13
|
#
|
@@ -18,26 +19,33 @@ module Authlogic
|
|
18
19
|
#
|
19
20
|
# === Setting up
|
20
21
|
#
|
21
|
-
# Authlogic comes with some simple testing tools. To get these, you need to
|
22
|
-
# you are doing this in a rails app,
|
22
|
+
# Authlogic comes with some simple testing tools. To get these, you need to
|
23
|
+
# first require Authlogic's TestCase. If you are doing this in a rails app,
|
24
|
+
# you would require this file at the top of your test_helper.rb file:
|
23
25
|
#
|
24
26
|
# require "authlogic/test_case"
|
25
27
|
#
|
26
|
-
# If you are using Test::Unit::TestCase, the standard testing library that
|
27
|
-
#
|
28
|
+
# If you are using Test::Unit::TestCase, the standard testing library that
|
29
|
+
# comes with ruby, then you can skip this next part. If you are not, you need
|
30
|
+
# to include the Authlogic::TestCase into your testing suite as follows:
|
28
31
|
#
|
29
32
|
# include Authlogic::TestCase
|
30
33
|
#
|
31
|
-
# Now that everything is ready to go, let's move onto actually testing. Here
|
34
|
+
# Now that everything is ready to go, let's move onto actually testing. Here
|
35
|
+
# is the basic idea behind testing:
|
32
36
|
#
|
33
|
-
# Authlogic requires a "connection" to your controller to activate it. In the
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
+
# Authlogic requires a "connection" to your controller to activate it. In the
|
38
|
+
# same manner that ActiveRecord requires a connection to your database. It
|
39
|
+
# can't do anything until it gets connected. That being said, Authlogic will
|
40
|
+
# raise an Authlogic::Session::Activation::NotActivatedError any time you try
|
41
|
+
# to instantiate an object without a "connection". So before you do anything
|
42
|
+
# with Authlogic, you need to activate / connect Authlogic. Let's walk through
|
43
|
+
# how to do this in tests:
|
37
44
|
#
|
38
45
|
# === Fixtures / Factories
|
39
46
|
#
|
40
|
-
# Creating users via fixtures / factories is easy. Here's an example of a
|
47
|
+
# Creating users via fixtures / factories is easy. Here's an example of a
|
48
|
+
# fixture:
|
41
49
|
#
|
42
50
|
# ben:
|
43
51
|
# email: whatever@whatever.com
|
@@ -47,43 +55,52 @@ module Authlogic
|
|
47
55
|
# single_access_token: <%= Authlogic::Random.friendly_token %>
|
48
56
|
# perishable_token: <%= Authlogic::Random.friendly_token %>
|
49
57
|
#
|
50
|
-
# Notice the crypted_password value. Just supplement that with whatever crypto
|
58
|
+
# Notice the crypted_password value. Just supplement that with whatever crypto
|
59
|
+
# provider you are using, if you are not using the default.
|
51
60
|
#
|
52
61
|
# === Functional tests
|
53
62
|
#
|
54
|
-
# Activating Authlogic isn't a problem here, because making a request will
|
55
|
-
#
|
63
|
+
# Activating Authlogic isn't a problem here, because making a request will
|
64
|
+
# activate Authlogic for you. The problem is logging users in so they can
|
65
|
+
# access restricted areas. Solving this is simple, just do this:
|
56
66
|
#
|
57
67
|
# setup :activate_authlogic
|
58
68
|
#
|
59
|
-
# For those of you unfamiliar with TestUnit, the setup method basically just
|
60
|
-
# It is essentially "setting up"
|
69
|
+
# For those of you unfamiliar with TestUnit, the setup method basically just
|
70
|
+
# executes a method before any test is ran. It is essentially "setting up"
|
71
|
+
# your tests.
|
61
72
|
#
|
62
73
|
# Once you have done this, just log users in like usual:
|
63
74
|
#
|
64
75
|
# UserSession.create(users(:whomever))
|
65
76
|
# # access my restricted area here
|
66
77
|
#
|
67
|
-
# Do this before you make your request and it will act as if that user is
|
78
|
+
# Do this before you make your request and it will act as if that user is
|
79
|
+
# logged in.
|
68
80
|
#
|
69
81
|
# === Integration tests
|
70
82
|
#
|
71
|
-
# Again, just like functional tests, you don't have to do anything. As soon as
|
72
|
-
#
|
83
|
+
# Again, just like functional tests, you don't have to do anything. As soon as
|
84
|
+
# you make a request, Authlogic will be connected. If you want to activate
|
85
|
+
# Authlogic before making a request follow the same steps described in the
|
73
86
|
# "functional tests" section above. It works in the same manner.
|
74
87
|
#
|
75
88
|
# === Unit tests
|
76
89
|
#
|
77
|
-
# The only time you need to do any trickiness here is if you want to test
|
78
|
-
#
|
90
|
+
# The only time you need to do any trickiness here is if you want to test
|
91
|
+
# Authlogic models. Maybe you added some custom code or methods in your
|
92
|
+
# Authlogic models. Maybe you are writing a plugin or a library that extends
|
93
|
+
# Authlogic.
|
79
94
|
#
|
80
|
-
# That being said, in this environment there is no controller. So you need to
|
81
|
-
# that looks like a controller, acts like a
|
82
|
-
#
|
83
|
-
#
|
95
|
+
# That being said, in this environment there is no controller. So you need to
|
96
|
+
# use a "mock" controller. Something that looks like a controller, acts like a
|
97
|
+
# controller, but isn't a "real" controller. You are essentially connecting
|
98
|
+
# Authlogic to your "mock" controller, then you can test off of the mock
|
99
|
+
# controller to make sure everything is functioning properly.
|
84
100
|
#
|
85
|
-
# I use a mock controller to test Authlogic myself. It's part of the Authlogic
|
86
|
-
#
|
101
|
+
# I use a mock controller to test Authlogic myself. It's part of the Authlogic
|
102
|
+
# library that you can easily use. It's as simple as functional and
|
103
|
+
# integration tests. Just do the following:
|
87
104
|
#
|
88
105
|
# setup :activate_authlogic
|
89
106
|
#
|
@@ -94,9 +111,11 @@ module Authlogic
|
|
94
111
|
# assert UserSession.create(ben)
|
95
112
|
# assert_equal controller.session["user_credentials"], ben.persistence_token
|
96
113
|
#
|
97
|
-
# See how I am checking that Authlogic is interacting with the controller
|
114
|
+
# See how I am checking that Authlogic is interacting with the controller
|
115
|
+
# properly? That's the idea here.
|
98
116
|
module TestCase
|
99
|
-
# Activates authlogic so that you can use it in your tests. You should call
|
117
|
+
# Activates authlogic so that you can use it in your tests. You should call
|
118
|
+
# this method in your test's setup. Ex:
|
100
119
|
#
|
101
120
|
# setup :activate_authlogic
|
102
121
|
def activate_authlogic
|
@@ -109,8 +128,9 @@ module Authlogic
|
|
109
128
|
Authlogic::Session::Base.controller = (@request && Authlogic::TestCase::RailsRequestAdapter.new(@request)) || controller
|
110
129
|
end
|
111
130
|
|
112
|
-
# The Authlogic::TestCase::MockController object passed to Authlogic to
|
113
|
-
# See the module description
|
131
|
+
# The Authlogic::TestCase::MockController object passed to Authlogic to
|
132
|
+
# activate it. You can access this in your test. See the module description
|
133
|
+
# for an example.
|
114
134
|
def controller
|
115
135
|
@controller ||= Authlogic::TestCase::MockController.new
|
116
136
|
end
|
data/lib/authlogic.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# Authlogic uses ActiveSupport's core extensions like `strip_heredoc`, which
|
2
|
+
# ActiveRecord does not `require`. It's possible that we could save a few
|
3
|
+
# milliseconds by loading only the specific core extensions we need, but
|
4
|
+
# `all.rb` is simpler. We can revisit this decision if it becomes a problem.
|
5
|
+
require "active_support/all"
|
6
|
+
|
1
7
|
require "active_record"
|
2
8
|
|
3
9
|
path = File.dirname(__FILE__) + "/authlogic/"
|
@@ -9,6 +9,7 @@ module ActsAsAuthenticTest
|
|
9
9
|
"dakota.dux+1@gmail.com",
|
10
10
|
"dakota.d'ux@gmail.com",
|
11
11
|
"a&b@c.com",
|
12
|
+
"someuser@somedomain.travelersinsurance"
|
12
13
|
]
|
13
14
|
|
14
15
|
BAD_ASCII_EMAILS = [
|
@@ -17,6 +18,7 @@ module ActsAsAuthenticTest
|
|
17
18
|
"question?mark@gmail.com",
|
18
19
|
"backslash@g\\mail.com",
|
19
20
|
"<script>alert(123);</script>\nnobody@example.com",
|
21
|
+
"someuser@somedomain.isreallytoolongandimeanreallytoolong"
|
20
22
|
]
|
21
23
|
|
22
24
|
# http://en.wikipedia.org/wiki/ISO/IEC_8859-1#Codepage_layout
|
@@ -58,7 +60,7 @@ module ActsAsAuthenticTest
|
|
58
60
|
"我>.香港", # greater than
|
59
61
|
"我?本@屋企.香港", # question mark
|
60
62
|
"чебурша@ьн\\ами.рф", # backslash
|
61
|
-
"user@domain.com%0A<script>alert('hello')</script>"
|
63
|
+
"user@domain.com%0A<script>alert('hello')</script>"
|
62
64
|
]
|
63
65
|
|
64
66
|
def test_email_field_config
|
@@ -76,7 +78,7 @@ module ActsAsAuthenticTest
|
|
76
78
|
assert Employee.validate_email_field
|
77
79
|
|
78
80
|
User.validate_email_field = false
|
79
|
-
|
81
|
+
refute User.validate_email_field
|
80
82
|
User.validate_email_field true
|
81
83
|
assert User.validate_email_field
|
82
84
|
end
|
@@ -94,25 +96,25 @@ module ActsAsAuthenticTest
|
|
94
96
|
def test_validates_format_of_email_field_options_config
|
95
97
|
default = {
|
96
98
|
:with => Authlogic::Regex.email,
|
97
|
-
:message =>
|
99
|
+
:message => proc do
|
98
100
|
I18n.t(
|
99
101
|
'error_messages.email_invalid',
|
100
102
|
:default => "should look like an email address."
|
101
103
|
)
|
102
104
|
end
|
103
105
|
}
|
104
|
-
|
106
|
+
default_message = default.delete(:message).call
|
105
107
|
|
106
108
|
options = User.validates_format_of_email_field_options
|
107
109
|
message = options.delete(:message)
|
108
|
-
assert message.
|
109
|
-
assert_equal
|
110
|
+
assert message.is_a?(Proc)
|
111
|
+
assert_equal default_message, message.call
|
110
112
|
assert_equal default, options
|
111
113
|
|
112
114
|
options = Employee.validates_format_of_email_field_options
|
113
115
|
message = options.delete(:message)
|
114
|
-
assert message.
|
115
|
-
assert_equal
|
116
|
+
assert message.is_a?(Proc)
|
117
|
+
assert_equal default_message, message.call
|
116
118
|
assert_equal default, options
|
117
119
|
|
118
120
|
User.validates_format_of_email_field_options = { :yes => "no" }
|
@@ -155,7 +157,11 @@ module ActsAsAuthenticTest
|
|
155
157
|
end
|
156
158
|
|
157
159
|
def test_validates_uniqueness_of_email_field_options_config
|
158
|
-
default = {
|
160
|
+
default = {
|
161
|
+
:case_sensitive => false,
|
162
|
+
:scope => Employee.validations_scope,
|
163
|
+
:if => "#{Employee.email_field}_changed?".to_sym
|
164
|
+
}
|
159
165
|
assert_equal default, Employee.validates_uniqueness_of_email_field_options
|
160
166
|
|
161
167
|
Employee.validates_uniqueness_of_email_field_options = { :yes => "no" }
|
@@ -167,43 +173,43 @@ module ActsAsAuthenticTest
|
|
167
173
|
def test_validates_length_of_email_field
|
168
174
|
u = User.new
|
169
175
|
u.email = "a@a.a"
|
170
|
-
|
171
|
-
|
176
|
+
refute u.valid?
|
177
|
+
refute u.errors[:email].empty?
|
172
178
|
|
173
179
|
u.email = "a@a.com"
|
174
|
-
|
175
|
-
assert u.errors[:email].
|
180
|
+
refute u.valid?
|
181
|
+
assert u.errors[:email].empty?
|
176
182
|
end
|
177
183
|
|
178
184
|
def test_validates_format_of_email_field
|
179
185
|
u = User.new
|
180
186
|
u.email = "aaaaaaaaaaaaa"
|
181
187
|
u.valid?
|
182
|
-
|
188
|
+
refute u.errors[:email].empty?
|
183
189
|
|
184
190
|
u.email = "a@a.com"
|
185
191
|
u.valid?
|
186
|
-
assert u.errors[:email].
|
192
|
+
assert u.errors[:email].empty?
|
187
193
|
|
188
194
|
u.email = "damien+test1...etc..@mydomain.com"
|
189
195
|
u.valid?
|
190
|
-
assert u.errors[:email].
|
196
|
+
assert u.errors[:email].empty?
|
191
197
|
|
192
198
|
u.email = "dakota.dux+1@gmail.com"
|
193
199
|
u.valid?
|
194
|
-
assert u.errors[:email].
|
200
|
+
assert u.errors[:email].empty?
|
195
201
|
|
196
202
|
u.email = "dakota.d'ux@gmail.com"
|
197
203
|
u.valid?
|
198
|
-
assert u.errors[:email].
|
204
|
+
assert u.errors[:email].empty?
|
199
205
|
|
200
206
|
u.email = "<script>alert(123);</script>\nnobody@example.com"
|
201
|
-
|
202
|
-
|
207
|
+
refute u.valid?
|
208
|
+
refute u.errors[:email].empty?
|
203
209
|
|
204
210
|
u.email = "a&b@c.com"
|
205
211
|
u.valid?
|
206
|
-
assert u.errors[:email].
|
212
|
+
assert u.errors[:email].empty?
|
207
213
|
end
|
208
214
|
|
209
215
|
def test_validates_format_of_nonascii_email_field
|
@@ -219,16 +225,16 @@ module ActsAsAuthenticTest
|
|
219
225
|
def test_validates_uniqueness_of_email_field
|
220
226
|
u = User.new
|
221
227
|
u.email = "bjohnson@binarylogic.com"
|
222
|
-
|
223
|
-
|
228
|
+
refute u.valid?
|
229
|
+
refute u.errors[:email].empty?
|
224
230
|
|
225
231
|
u.email = "BJOHNSON@binarylogic.com"
|
226
|
-
|
227
|
-
|
232
|
+
refute u.valid?
|
233
|
+
refute u.errors[:email].empty?
|
228
234
|
|
229
235
|
u.email = "a@a.com"
|
230
|
-
|
231
|
-
assert u.errors[:email].
|
236
|
+
refute u.valid?
|
237
|
+
assert u.errors[:email].empty?
|
232
238
|
end
|
233
239
|
end
|
234
240
|
end
|
@@ -52,11 +52,11 @@ module ActsAsAuthenticTest
|
|
52
52
|
|
53
53
|
def test_logged_in_logged_out
|
54
54
|
u = User.first
|
55
|
-
|
55
|
+
refute u.logged_in?
|
56
56
|
assert u.logged_out?
|
57
57
|
u.last_request_at = Time.now
|
58
58
|
assert u.logged_in?
|
59
|
-
|
59
|
+
refute u.logged_out?
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|