authlogic 2.0.1 → 2.1.1

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.
Files changed (82) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG.rdoc +105 -1
  3. data/{MIT-LICENSE → LICENSE} +1 -1
  4. data/README.rdoc +137 -160
  5. data/Rakefile +47 -19
  6. data/VERSION.yml +4 -0
  7. data/authlogic.gemspec +205 -0
  8. data/init.rb +1 -1
  9. data/lib/authlogic/acts_as_authentic/base.rb +31 -17
  10. data/lib/authlogic/acts_as_authentic/email.rb +47 -14
  11. data/lib/authlogic/acts_as_authentic/logged_in_status.rb +20 -14
  12. data/lib/authlogic/acts_as_authentic/login.rb +84 -8
  13. data/lib/authlogic/acts_as_authentic/password.rb +207 -88
  14. data/lib/authlogic/acts_as_authentic/perishable_token.rb +9 -4
  15. data/lib/authlogic/acts_as_authentic/persistence_token.rb +4 -2
  16. data/lib/authlogic/acts_as_authentic/restful_authentication.rb +5 -4
  17. data/lib/authlogic/acts_as_authentic/session_maintenance.rb +16 -4
  18. data/lib/authlogic/acts_as_authentic/single_access_token.rb +10 -3
  19. data/lib/authlogic/acts_as_authentic/validations_scope.rb +1 -1
  20. data/lib/authlogic/controller_adapters/abstract_adapter.rb +13 -1
  21. data/lib/authlogic/controller_adapters/rails_adapter.rb +14 -4
  22. data/lib/authlogic/crypto_providers/aes256.rb +2 -2
  23. data/lib/authlogic/crypto_providers/bcrypt.rb +3 -1
  24. data/lib/authlogic/crypto_providers/sha1.rb +2 -2
  25. data/lib/authlogic/i18n.rb +10 -1
  26. data/lib/authlogic/regex.rb +25 -0
  27. data/lib/authlogic/session/activation.rb +2 -0
  28. data/lib/authlogic/session/active_record_trickery.rb +13 -3
  29. data/lib/authlogic/session/brute_force_protection.rb +26 -11
  30. data/lib/authlogic/session/callbacks.rb +16 -6
  31. data/lib/authlogic/session/cookies.rb +16 -12
  32. data/lib/authlogic/session/existence.rb +4 -2
  33. data/lib/authlogic/session/foundation.rb +1 -1
  34. data/lib/authlogic/session/http_auth.rb +46 -11
  35. data/lib/authlogic/session/magic_columns.rb +25 -6
  36. data/lib/authlogic/session/magic_states.rb +4 -4
  37. data/lib/authlogic/session/params.rb +14 -9
  38. data/lib/authlogic/session/password.rb +86 -24
  39. data/lib/authlogic/session/priority_record.rb +1 -1
  40. data/lib/authlogic/session/session.rb +2 -2
  41. data/lib/authlogic/session/timeout.rb +1 -1
  42. data/lib/authlogic/session/validation.rb +9 -5
  43. data/lib/authlogic/test_case/mock_controller.rb +45 -0
  44. data/lib/authlogic/test_case/mock_cookie_jar.rb +14 -0
  45. data/lib/authlogic/test_case/mock_logger.rb +10 -0
  46. data/lib/authlogic/test_case/mock_request.rb +19 -0
  47. data/lib/authlogic/test_case/rails_request_adapter.rb +30 -0
  48. data/lib/authlogic/test_case.rb +114 -0
  49. data/lib/authlogic.rb +1 -1
  50. data/rails/init.rb +1 -0
  51. data/shoulda_macros/authlogic.rb +2 -1
  52. data/test/acts_as_authentic_test/base_test.rb +6 -0
  53. data/test/acts_as_authentic_test/email_test.rb +25 -7
  54. data/test/acts_as_authentic_test/login_test.rb +37 -7
  55. data/test/acts_as_authentic_test/magic_columns_test.rb +4 -4
  56. data/test/acts_as_authentic_test/password_test.rb +46 -44
  57. data/test/acts_as_authentic_test/perishable_token_test.rb +36 -2
  58. data/test/acts_as_authentic_test/restful_authentication_test.rb +40 -0
  59. data/test/acts_as_authentic_test/session_maintenance_test.rb +26 -18
  60. data/test/acts_as_authentic_test/single_access_test.rb +6 -1
  61. data/test/libs/affiliate.rb +7 -0
  62. data/test/libs/ldaper.rb +3 -0
  63. data/test/random_test.rb +2 -2
  64. data/test/session_test/activation_test.rb +1 -1
  65. data/test/session_test/active_record_trickery_test.rb +2 -1
  66. data/test/session_test/brute_force_protection_test.rb +34 -9
  67. data/test/session_test/cookies_test.rb +3 -3
  68. data/test/session_test/http_auth_test.rb +20 -8
  69. data/test/session_test/magic_columns_test.rb +7 -4
  70. data/test/session_test/magic_states_test.rb +3 -3
  71. data/test/session_test/params_test.rb +4 -4
  72. data/test/session_test/password_test.rb +8 -0
  73. data/test/session_test/session_test.rb +9 -9
  74. data/test/session_test/timeout_test.rb +10 -1
  75. data/test/test_helper.rb +43 -21
  76. metadata +38 -27
  77. data/Manifest.txt +0 -111
  78. data/lib/authlogic/testing/test_unit_helpers.rb +0 -39
  79. data/lib/authlogic/version.rb +0 -56
  80. data/test/libs/mock_controller.rb +0 -35
  81. data/test/libs/mock_cookie_jar.rb +0 -10
  82. data/test/libs/mock_request.rb +0 -5
@@ -29,40 +29,72 @@ module Authlogic
29
29
  # end
30
30
  # end
31
31
  #
32
- # * <tt>Default:</tt> "find_by_#{login_field}"
32
+ # Now just specify the name of this method for this configuration option and you are all set. You can do anything you want here. Maybe you allow users to have multiple logins
33
+ # and you want to search a has_many relationship, etc. The sky is the limit.
34
+ #
35
+ # * <tt>Default:</tt> "find_by_smart_case_login_field"
33
36
  # * <tt>Accepts:</tt> Symbol or String
34
37
  def find_by_login_method(value = nil)
35
- config(:find_by_login_method, value, "find_by_#{login_field}")
38
+ rw_config(:find_by_login_method, value, "find_by_smart_case_login_field")
36
39
  end
37
40
  alias_method :find_by_login_method=, :find_by_login_method
38
41
 
42
+ # The text used to identify credentials (username/password) combination when a bad login attempt occurs.
43
+ # When you show error messages for a bad login, it's considered good security practice to hide which field
44
+ # the user has entered incorrectly (the login field or the password field). For a full explanation, see
45
+ # http://www.gnucitizen.org/blog/username-enumeration-vulnerabilities/
46
+ #
47
+ # Example of use:
48
+ #
49
+ # class UserSession < Authlogic::Session::Base
50
+ # generalize_credentials_error_messages true
51
+ # end
52
+ #
53
+ # This would make the error message for bad logins and bad passwords look identical:
54
+ #
55
+ # Login/Password combination is not valid
56
+ #
57
+ # The downside to enabling this is that is can be too vague for a user that has a hard time remembering
58
+ # their username and password combinations. It also disables the ability to to highlight the field
59
+ # with the error when you use form_for.
60
+ #
61
+ # If you are developing an app where security is an extreme priority (such as a financial application),
62
+ # then you should enable this. Otherwise, leaving this off is fine.
63
+ #
64
+ # * <tt>Default</tt> false
65
+ # * <tt>Accepts:</tt> Boolean
66
+ def generalize_credentials_error_messages(value = nil)
67
+ rw_config(:generalize_credentials_error_messages, value, false)
68
+ end
69
+ alias_method :generalize_credentials_error_messages=, :generalize_credentials_error_messages
70
+
39
71
  # The name of the method you want Authlogic to create for storing the login / username. Keep in mind this is just for your
40
72
  # Authlogic::Session, if you want it can be something completely different than the field in your model. So if you wanted people to
41
73
  # login with a field called "login" and then find users by email this is compeltely doable. See the find_by_login_method configuration
42
74
  # option for more details.
43
75
  #
44
- # * <tt>Default:</tt> Uses the configuration option in your model: User.login_field
76
+ # * <tt>Default:</tt> klass.login_field || klass.email_field
45
77
  # * <tt>Accepts:</tt> Symbol or String
46
78
  def login_field(value = nil)
47
- config(:login_field, value, klass.login_field || klass.email_field)
79
+ rw_config(:login_field, value, klass.login_field || klass.email_field)
48
80
  end
49
81
  alias_method :login_field=, :login_field
50
82
 
51
- # Works exactly like login_field, but for the password instead.
83
+ # Works exactly like login_field, but for the password instead. Returns :password if a login_field exists.
52
84
  #
53
85
  # * <tt>Default:</tt> :password
54
86
  # * <tt>Accepts:</tt> Symbol or String
55
87
  def password_field(value = nil)
56
- config(:password_field, value, :password)
88
+ rw_config(:password_field, value, login_field && :password)
57
89
  end
58
90
  alias_method :password_field=, :password_field
59
91
 
60
92
  # The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
61
93
  #
62
- # * <tt>Default:</tt> "valid_#{password_field}?"
94
+ # * <tt>Default:</tt> "valid_password?"
63
95
  # * <tt>Accepts:</tt> Symbol or String
64
96
  def verify_password_method(value = nil)
65
- config(:verify_password_method, value, "valid_#{password_field}?")
97
+ rw_config(:verify_password_method, value, "valid_password?")
66
98
  end
67
99
  alias_method :verify_password_method=, :verify_password_method
68
100
  end
@@ -71,18 +103,23 @@ module Authlogic
71
103
  module InstanceMethods
72
104
  def initialize(*args)
73
105
  if !self.class.configured_password_methods
74
- self.class.send(:attr_writer, login_field) if !respond_to?("#{login_field}=")
75
- self.class.send(:attr_reader, login_field) if !respond_to?(login_field)
76
- self.class.send(:attr_writer, password_field) if !respond_to?("#{password_field}=")
77
- self.class.send(:define_method, password_field) {} if !respond_to?(password_field)
106
+ if login_field
107
+ self.class.send(:attr_writer, login_field) if !respond_to?("#{login_field}=")
108
+ self.class.send(:attr_reader, login_field) if !respond_to?(login_field)
109
+ end
110
+
111
+ if password_field
112
+ self.class.send(:attr_writer, password_field) if !respond_to?("#{password_field}=")
113
+ self.class.send(:define_method, password_field) {} if !respond_to?(password_field)
78
114
 
79
- self.class.class_eval <<-"end_eval", __FILE__, __LINE__
80
- private
81
- # The password should not be accessible publicly. This way forms using form_for don't fill the password with the attempted password. The prevent this we just create this method that is private.
82
- def protected_#{password_field}
83
- @#{password_field}
84
- end
85
- end_eval
115
+ self.class.class_eval <<-"end_eval", __FILE__, __LINE__
116
+ private
117
+ # The password should not be accessible publicly. This way forms using form_for don't fill the password with the attempted password. To prevent this we just create this method that is private.
118
+ def protected_#{password_field}
119
+ @#{password_field}
120
+ end
121
+ end_eval
122
+ end
86
123
 
87
124
  self.class.configured_password_methods = true
88
125
  end
@@ -90,6 +127,7 @@ module Authlogic
90
127
  super
91
128
  end
92
129
 
130
+ # Returns the login_field / password_field credentials combination in hash form.
93
131
  def credentials
94
132
  if authenticating_with_password?
95
133
  details = {}
@@ -101,6 +139,7 @@ module Authlogic
101
139
  end
102
140
  end
103
141
 
142
+ # Accepts the login_field / password_field credentials combination in hash form.
104
143
  def credentials=(value)
105
144
  super
106
145
  values = value.is_a?(Array) ? value : [value]
@@ -112,29 +151,44 @@ module Authlogic
112
151
  end
113
152
  end
114
153
 
154
+ def invalid_password?
155
+ invalid_password == true
156
+ end
157
+
115
158
  private
116
159
  def authenticating_with_password?
117
- !send(login_field).nil? || !send("protected_#{password_field}").nil?
160
+ login_field && (!send(login_field).nil? || !send("protected_#{password_field}").nil?)
118
161
  end
119
162
 
120
163
  def validate_by_password
121
- errors.add(login_field, I18n.t('error_messages.login_blank', :default => "can not be blank")) if send(login_field).blank?
122
- errors.add(password_field, I18n.t('error_messages.password_blank', :default => "can not be blank")) if send("protected_#{password_field}").blank?
164
+ self.invalid_password = false
165
+
166
+ errors.add(login_field, I18n.t('error_messages.login_blank', :default => "cannot be blank")) if send(login_field).blank?
167
+ errors.add(password_field, I18n.t('error_messages.password_blank', :default => "cannot be blank")) if send("protected_#{password_field}").blank?
123
168
  return if errors.count > 0
124
169
 
125
170
  self.attempted_record = search_for_record(find_by_login_method, send(login_field))
126
171
 
127
172
  if attempted_record.blank?
128
- errors.add(login_field, I18n.t('error_messages.login_not_found', :default => "does not exist"))
173
+ generalize_credentials_error_messages? ? add_general_credentials_error : errors.add(login_field, I18n.t('error_messages.login_not_found', :default => "is not valid"))
129
174
  return
130
175
  end
131
176
 
132
177
  if !attempted_record.send(verify_password_method, send("protected_#{password_field}"))
133
- errors.add(password_field, I18n.t('error_messages.password_invalid', :default => "is not valid"))
178
+ self.invalid_password = true
179
+ generalize_credentials_error_messages? ? add_general_credentials_error : errors.add(password_field, I18n.t('error_messages.password_invalid', :default => "is not valid"))
134
180
  return
135
181
  end
136
182
  end
137
183
 
184
+ def invalid_password
185
+ @invalid_password
186
+ end
187
+
188
+ def invalid_password=(value)
189
+ @invalid_password = value
190
+ end
191
+
138
192
  def find_by_login_method
139
193
  self.class.find_by_login_method
140
194
  end
@@ -143,6 +197,14 @@ module Authlogic
143
197
  self.class.login_field
144
198
  end
145
199
 
200
+ def add_general_credentials_error
201
+ errors.add(:base, I18n.t('error_messages.general_credentials_error', :default => "#{login_field.to_s.humanize}/Password combination is not valid"))
202
+ end
203
+
204
+ def generalize_credentials_error_messages?
205
+ self.class.generalize_credentials_error_messages == true
206
+ end
207
+
146
208
  def password_field
147
209
  self.class.password_field
148
210
  end
@@ -16,7 +16,7 @@ module Authlogic
16
16
  def credentials=(value)
17
17
  super
18
18
  values = value.is_a?(Array) ? value : [value]
19
- self.priority_record = values.second if values.second.class < ::ActiveRecord::Base
19
+ self.priority_record = values[1] if values[1].class < ::ActiveRecord::Base
20
20
  end
21
21
 
22
22
  private
@@ -20,7 +20,7 @@ module Authlogic
20
20
  # * <tt>Default:</tt> cookie_key
21
21
  # * <tt>Accepts:</tt> Symbol or String
22
22
  def session_key(value = nil)
23
- config(:session_key, value, cookie_key)
23
+ rw_config(:session_key, value, cookie_key)
24
24
  end
25
25
  alias_method :session_key=, :session_key
26
26
  end
@@ -49,7 +49,7 @@ module Authlogic
49
49
  def session_key
50
50
  build_key(self.class.session_key)
51
51
  end
52
-
52
+
53
53
  def update_session
54
54
  controller.session[session_key] = record && record.persistence_token
55
55
  controller.session["#{session_key}_#{klass.primary_key}"] = record && record.send(record.class.primary_key)
@@ -48,7 +48,7 @@ module Authlogic
48
48
  # * <tt>Default:</tt> false
49
49
  # * <tt>Accepts:</tt> Boolean
50
50
  def logout_on_timeout(value = nil)
51
- config(:logout_on_timeout, value, false)
51
+ rw_config(:logout_on_timeout, value, false)
52
52
  end
53
53
  alias_method :logout_on_timeout=, :logout_on_timeout
54
54
  end
@@ -10,10 +10,14 @@ module Authlogic
10
10
  # private
11
11
  # def check_if_awesome
12
12
  # errors.add(:login, "must contain awesome") if login && !login.include?("awesome")
13
- # errors.add_to_base("You must be awesome to log in") unless record.awesome?
13
+ # errors.add(:base, "You must be awesome to log in") unless attempted_record.awesome?
14
14
  # end
15
15
  # end
16
16
  class Errors < ::ActiveRecord::Errors
17
+ def [](key)
18
+ value = super
19
+ value.is_a?(Array) ? value : [value].compact
20
+ end
17
21
  end
18
22
 
19
23
  # You should use this as a place holder for any records that you find during validation. The main reason for this is to
@@ -39,7 +43,7 @@ module Authlogic
39
43
  # private
40
44
  # def check_if_awesome
41
45
  # errors.add(:login, "must contain awesome") if login && !login.include?("awesome")
42
- # errors.add_to_base("You must be awesome to log in") unless record.awesome?
46
+ # errors.add(:base, "You must be awesome to log in") unless attempted_record.awesome?
43
47
  # end
44
48
  # end
45
49
  def errors
@@ -58,18 +62,18 @@ module Authlogic
58
62
  validate
59
63
  ensure_authentication_attempted
60
64
 
61
- if errors.empty?
65
+ if errors.size == 0
62
66
  new_session? ? after_validation_on_create : after_validation_on_update
63
67
  after_validation
64
68
  end
65
69
 
66
70
  save_record(attempted_record)
67
- errors.empty?
71
+ errors.size == 0
68
72
  end
69
73
 
70
74
  private
71
75
  def ensure_authentication_attempted
72
- errors.add_to_base(I18n.t('error_messages.no_authentication_details', :default => "You did not provide any details for authentication.")) if errors.empty? && attempted_record.nil?
76
+ errors.add(:base, I18n.t('error_messages.no_authentication_details', :default => "You did not provide any details for authentication.")) if errors.empty? && attempted_record.nil?
73
77
  end
74
78
  end
75
79
  end
@@ -0,0 +1,45 @@
1
+ module Authlogic
2
+ module TestCase
3
+ # Basically acts like a controller but doesn't do anything. Authlogic can interact with this, do it's thing and then you
4
+ # can look at the controller object to see if anything changed.
5
+ class MockController < ControllerAdapters::AbstractAdapter
6
+ attr_accessor :http_user, :http_password
7
+ attr_writer :request_content_type
8
+
9
+ def initialize
10
+ end
11
+
12
+ def authenticate_with_http_basic(&block)
13
+ yield http_user, http_password
14
+ end
15
+
16
+ def cookies
17
+ @cookies ||= MockCookieJar.new
18
+ end
19
+
20
+ def cookie_domain
21
+ nil
22
+ end
23
+
24
+ def logger
25
+ @logger ||= MockLogger.new
26
+ end
27
+
28
+ def params
29
+ @params ||= {}
30
+ end
31
+
32
+ def request
33
+ @request ||= MockRequest.new(controller)
34
+ end
35
+
36
+ def request_content_type
37
+ @request_content_type ||= "text/html"
38
+ end
39
+
40
+ def session
41
+ @session ||= {}
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ module Authlogic
2
+ module TestCase
3
+ class MockCookieJar < Hash # :nodoc:
4
+ def [](key)
5
+ hash = super
6
+ hash && hash[:value]
7
+ end
8
+
9
+ def delete(key, options = {})
10
+ super(key)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Authlogic
2
+ module TestCase
3
+ # Simple class to replace real loggers, so that we can raise any errors being logged.
4
+ class MockLogger
5
+ def error(message)
6
+ raise message
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ module Authlogic
2
+ module TestCase
3
+ class MockRequest # :nodoc:
4
+ attr_accessor :controller
5
+
6
+ def initialize(controller)
7
+ self.controller = controller
8
+ end
9
+
10
+ def remote_ip
11
+ (controller && controller.respond_to?(:env) && controller.env.is_a?(Hash) && controller.env['REMOTE_ADDR']) || "1.1.1.1"
12
+ end
13
+
14
+ private
15
+ def method_missing(*args, &block)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ module Authlogic
2
+ module TestCase
3
+ # Adapts authlogic to work with the @request object when testing. This way Authlogic can set cookies and what not before
4
+ # a request is made, ultimately letting you log in users in functional tests.
5
+ class RailsRequestAdapter < ControllerAdapters::AbstractAdapter
6
+ def authenticate_with_http_basic(&block)
7
+ end
8
+
9
+ def cookies
10
+ new_cookies = MockCookieJar.new
11
+ super.each do |key, value|
12
+ new_cookies[key] = value[:value]
13
+ end
14
+ new_cookies
15
+ end
16
+
17
+ def cookie_domain
18
+ nil
19
+ end
20
+
21
+ def request
22
+ @request ||= MockRequest.new(controller)
23
+ end
24
+
25
+ def request_content_type
26
+ request.format.to_s
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,114 @@
1
+ require File.dirname(__FILE__) + "/test_case/rails_request_adapter"
2
+ require File.dirname(__FILE__) + "/test_case/mock_cookie_jar"
3
+ require File.dirname(__FILE__) + "/test_case/mock_controller"
4
+ require File.dirname(__FILE__) + "/test_case/mock_logger"
5
+ require File.dirname(__FILE__) + "/test_case/mock_request"
6
+
7
+ module Authlogic
8
+ # This module is a collection of methods and classes that help you easily test Authlogic. In fact,
9
+ # I use these same tools to test the internals of Authlogic.
10
+ #
11
+ # === The quick and dirty
12
+ #
13
+ # require "authlogic/test_case" # include at the top of test_helper.rb
14
+ # setup :activate_authlogic # run before tests are executed
15
+ # UserSession.create(users(:whomever)) # logs a user in
16
+ #
17
+ # For a more detailed explanation, see below.
18
+ #
19
+ # === Setting up
20
+ #
21
+ # Authlogic comes with some simple testing tools. To get these, you need to first require Authlogic's TestCase. If
22
+ # you are doing this in a rails app, you would require this file at the top of your test_helper.rb file:
23
+ #
24
+ # require "authlogic/test_case"
25
+ #
26
+ # If you are using Test::Unit::TestCase, the standard testing library that comes with ruby, then you can skip this next part.
27
+ # If you are not, you need to include the Authlogic::TestCase into your testing suite as follows:
28
+ #
29
+ # include Authlogic::TestCase
30
+ #
31
+ # Now that everything is ready to go, let's move onto actually testing. Here is the basic idea behind testing:
32
+ #
33
+ # Authlogic requires a "connection" to your controller to activate it. In the same manner that ActiveRecord requires a connection to
34
+ # your database. It can't do anything until it gets connnected. That being said, Authlogic will raise an
35
+ # Authlogic::Session::Activation::NotActivatedError any time you try to instantiate an object without a "connection".
36
+ # So before you do anything with Authlogic, you need to activate / connect Authlogic. Let's walk through how to do this in tests:
37
+ #
38
+ # === Fixtures / Factories
39
+ #
40
+ # Creating users via fixtures / factories is easy. Here's an example of a fixture:
41
+ #
42
+ # ben:
43
+ # email: whatever@whatever.com
44
+ # password_salt: <%= salt = Authlogic::Random.hex_token %>
45
+ # crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("benrocks" + salt) %>
46
+ # persistence_token: <%= Authlogic::Random.hex_token %>
47
+ # single_access_token: <%= Authlogic::Random.friendly_token %>
48
+ # perishable_token: <%= Authlogic::Random.friendly_token %>
49
+ #
50
+ # Notice the crypted_password value. Just supplement that with whatever crypto provider you are using, if you are not using the default.
51
+ #
52
+ # === Functional tests
53
+ #
54
+ # Activating Authlogic isn't a problem here, because making a request will activate Authlogic for you. The problem is
55
+ # logging users in so they can access restricted areas. Solving this is simple, just do this:
56
+ #
57
+ # setup :activate_authlogic
58
+ #
59
+ # For those of you unfamiliar with TestUnit, the setup method bascially just executes a method before any test is ran.
60
+ # It is essentially "setting up" your tests.
61
+ #
62
+ # Once you have done this, just log users in like usual:
63
+ #
64
+ # UserSession.create(users(:whomever))
65
+ # # access my restricted area here
66
+ #
67
+ # Do this before you make your request and it will act as if that user is logged in.
68
+ #
69
+ # === Integration tests
70
+ #
71
+ # Again, just like functional tests, you don't have to do anything. As soon as you make a request, Authlogic will be
72
+ # conntected. If you want to activate Authlogic before making a request follow the same steps described in the
73
+ # "functional tests" section above. It works in the same manner.
74
+ #
75
+ # === Unit tests
76
+ #
77
+ # The only time you need to do any trickiness here is if you want to test Authlogic models. Maybe you added some custom
78
+ # code or methods in your Authlogic models. Maybe you are writing a plugin or a library that extends Authlogic.
79
+ #
80
+ # That being said, in this environment there is no controller. So you need to use a "mock" controller. Something
81
+ # that looks like a controller, acts like a controller, but isn't a "real" controller. You are essentially connecting
82
+ # Authlogic to your "mock" controller, then you can test off of the mock controller to make sure everything is functioning
83
+ # properly.
84
+ #
85
+ # I use a mock controller to test Authlogic myself. It's part of the Authlogic library that you can easily use. It's as simple
86
+ # as functional and integration tests. Just do the following:
87
+ #
88
+ # setup :activate_authlogic
89
+ #
90
+ # You also get a controller method that you can test off of. For example:
91
+ #
92
+ # ben = users(:ben)
93
+ # assert_nil controller.session["user_credentials"]
94
+ # assert UserSession.create(ben)
95
+ # assert_equal controller.session["user_credentials"], ben.persistence_token
96
+ #
97
+ # See how I am checking that Authlogic is interacting with the controller properly? That's the idea here.
98
+ module TestCase
99
+ # Activates authlogic so that you can use it in your tests. You should call this method in your test's setup. Ex:
100
+ #
101
+ # setup :activate_authlogic
102
+ def activate_authlogic
103
+ Authlogic::Session::Base.controller = (@request && Authlogic::TestCase::RailsRequestAdapter.new(@request)) || controller
104
+ end
105
+
106
+ # The Authlogic::TestCase::MockController object passed to Authlogic to activate it. You can access this in your test.
107
+ # See the module description for an example.
108
+ def controller
109
+ @controller ||= Authlogic::TestCase::MockController.new
110
+ end
111
+ end
112
+
113
+ ::Test::Unit::TestCase.send(:include, TestCase) if defined?(::Test::Unit::TestCase)
114
+ end
data/lib/authlogic.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  require "active_support"
2
2
 
3
- require File.dirname(__FILE__) + "/authlogic/version"
4
3
  require File.dirname(__FILE__) + "/authlogic/i18n"
5
4
  require File.dirname(__FILE__) + "/authlogic/random"
5
+ require File.dirname(__FILE__) + "/authlogic/regex"
6
6
 
7
7
  require File.dirname(__FILE__) + "/authlogic/controller_adapters/abstract_adapter"
8
8
  require File.dirname(__FILE__) + "/authlogic/controller_adapters/rails_adapter" if defined?(Rails)
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "authlogic"
@@ -4,7 +4,8 @@ module Authlogic
4
4
  def self.should_be_authentic
5
5
  klass = model_class
6
6
  should "acts as authentic" do
7
- assert klass.respond_to?(:acts_as_authentic_config)
7
+ assert klass.new.respond_to?(:password=)
8
+ assert klass.new.respond_to?(:valid_password?)
8
9
  end
9
10
  end
10
11
  end
@@ -8,5 +8,11 @@ module ActsAsAuthenticTest
8
8
  end
9
9
  end
10
10
  end
11
+
12
+ def test_acts_as_authentic_with_old_config
13
+ assert_raise(ArgumentError) do
14
+ User.acts_as_authentic({})
15
+ end
16
+ end
11
17
  end
12
18
  end
@@ -33,7 +33,7 @@ module ActsAsAuthenticTest
33
33
  end
34
34
 
35
35
  def test_validates_format_of_email_field_options_config
36
- default = {:with => User.send(:email_regex), :message => I18n.t('error_messages.email_invalid', :default => "should look like an email address.")}
36
+ default = {:with => Authlogic::Regex.email, :message => I18n.t('error_messages.email_invalid', :default => "should look like an email address.")}
37
37
  assert_equal default, User.validates_format_of_email_field_options
38
38
  assert_equal default, Employee.validates_format_of_email_field_options
39
39
 
@@ -43,37 +43,55 @@ module ActsAsAuthenticTest
43
43
  assert_equal default, User.validates_format_of_email_field_options
44
44
  end
45
45
 
46
+ def test_validates_uniqueness_of_email_field_options_config
47
+ default = {:case_sensitive => false, :scope => Employee.validations_scope, :if => "#{Employee.email_field}_changed?".to_sym}
48
+ assert_equal default, Employee.validates_uniqueness_of_email_field_options
49
+
50
+ Employee.validates_uniqueness_of_email_field_options = {:yes => "no"}
51
+ assert_equal({:yes => "no"}, Employee.validates_uniqueness_of_email_field_options)
52
+ Employee.validates_uniqueness_of_email_field_options default
53
+ assert_equal default, Employee.validates_uniqueness_of_email_field_options
54
+ end
55
+
46
56
  def test_validates_length_of_email_field
47
57
  u = User.new
48
58
  u.email = "a@a.a"
49
59
  assert !u.valid?
50
- assert u.errors.on(:email)
60
+ assert u.errors[:email].size > 0
51
61
 
52
62
  u.email = "a@a.com"
53
63
  assert !u.valid?
54
- assert !u.errors.on(:email)
64
+ assert u.errors[:email].size == 0
55
65
  end
56
66
 
57
67
  def test_validates_format_of_email_field
58
68
  u = User.new
59
69
  u.email = "aaaaaaaaaaaaa"
60
70
  assert !u.valid?
61
- assert u.errors.on(:email)
71
+ assert u.errors[:email].size > 0
62
72
 
63
73
  u.email = "a@a.com"
64
74
  assert !u.valid?
65
- assert !u.errors.on(:email)
75
+ assert u.errors[:email].size == 0
76
+
77
+ u.email = "dakota.dux+1@gmail.com"
78
+ assert !u.valid?
79
+ assert u.errors[:email].size == 0
66
80
  end
67
81
 
68
82
  def test_validates_uniqueness_of_email_field
69
83
  u = User.new
70
84
  u.email = "bjohnson@binarylogic.com"
71
85
  assert !u.valid?
72
- assert u.errors.on(:email)
86
+ assert u.errors[:email].size > 0
87
+
88
+ u.email = "BJOHNSON@binarylogic.com"
89
+ assert !u.valid?
90
+ assert u.errors[:email].size > 0
73
91
 
74
92
  u.email = "a@a.com"
75
93
  assert !u.valid?
76
- assert !u.errors.on(:email)
94
+ assert u.errors[:email].size == 0
77
95
  end
78
96
  end
79
97
  end