authlogic 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of authlogic might be problematic. Click here for more details.

Files changed (26) hide show
  1. data/CHANGELOG.rdoc +8 -1
  2. data/Manifest +4 -0
  3. data/README.rdoc +50 -24
  4. data/authlogic.gemspec +5 -5
  5. data/lib/authlogic.rb +3 -0
  6. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb +34 -10
  7. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb +8 -3
  8. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb +2 -2
  9. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset.rb +73 -0
  10. data/lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb +27 -27
  11. data/lib/authlogic/session/base.rb +41 -38
  12. data/lib/authlogic/session/config.rb +125 -32
  13. data/lib/authlogic/session/password_reset.rb +17 -0
  14. data/lib/authlogic/session/scopes.rb +2 -6
  15. data/lib/authlogic/version.rb +2 -2
  16. data/test/fixtures/users.yml +3 -0
  17. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb +6 -1
  18. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb +16 -2
  19. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/password_reset_test.rb +40 -0
  20. data/test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb +1 -1
  21. data/test/session_tests/base_test.rb +9 -8
  22. data/test/session_tests/config_test.rb +84 -12
  23. data/test/session_tests/password_reset_test.rb +15 -0
  24. data/test/session_tests/scopes_test.rb +5 -4
  25. data/test/test_helper.rb +12 -6
  26. metadata +10 -2
@@ -1,6 +1,13 @@
1
- == 1.1.2 released 2008-11-13
1
+ == 1.2.0 released 2008-11-16
2
2
 
3
3
  * Added check for database set up in acts_as_authentic to prevent errors during migrations.
4
+ * Forced logged_in and logged_out named scopes to use seconds.
5
+ * Hardened valid_password? method to only allow raw passwords.
6
+ * controllers and scopes are no longer stored in class variables but in the Thread.current hash so their instances die out with the thread, which frees up memory.
7
+ * Removed single_access_token_field and remember_token_field from Sesson::Config, they are not needed there.
8
+ * Added password_reset_token to assist in resetting passwords.
9
+ * Added email_field, email_field_regex, email_field_regex_failed_message configuration options to acts_as_authentic. So that you can validate emails as well as a login, instead of the either-or approach.
10
+ * Added configuration for all validation messages for the session so that you can modify them and provide I18n support.
4
11
 
5
12
  == 1.1.1 released 2008-11-13
6
13
 
data/Manifest CHANGED
@@ -8,6 +8,7 @@ lib/authlogic/crypto_providers/sha512.rb
8
8
  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb
9
9
  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb
10
10
  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb
11
+ lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset.rb
11
12
  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb
12
13
  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb
13
14
  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb
@@ -21,6 +22,7 @@ lib/authlogic/session/config.rb
21
22
  lib/authlogic/session/cookies.rb
22
23
  lib/authlogic/session/errors.rb
23
24
  lib/authlogic/session/params.rb
25
+ lib/authlogic/session/password_reset.rb
24
26
  lib/authlogic/session/scopes.rb
25
27
  lib/authlogic/session/session.rb
26
28
  lib/authlogic/version.rb
@@ -42,6 +44,7 @@ test/libs/ordered_hash.rb
42
44
  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb
43
45
  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb
44
46
  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb
47
+ test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/password_reset_test.rb
45
48
  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb
46
49
  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb
47
50
  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/single_access_test.rb
@@ -52,6 +55,7 @@ test/session_tests/base_test.rb
52
55
  test/session_tests/config_test.rb
53
56
  test/session_tests/cookies_test.rb
54
57
  test/session_tests/params_test.rb
58
+ test/session_tests/password_reset_test.rb
55
59
  test/session_tests/scopes_test.rb
56
60
  test/session_tests/session_test.rb
57
61
  test/test_helper.rb
@@ -53,7 +53,7 @@ Or how about persisting the session...
53
53
  class ApplicationController
54
54
  helper_method :current_user_session, :current_user
55
55
 
56
- protected
56
+ private
57
57
  def current_user_session
58
58
  return @current_user_session if defined?(@current_user_session)
59
59
  @current_user_session = UserSession.find
@@ -71,6 +71,7 @@ Authlogic makes this a reality. This is just the tip of the ice berg. Keep readi
71
71
 
72
72
  * <b>Documentation:</b> http://authlogic.rubyforge.org
73
73
  * <b>Authlogic setup tutorial:</b> http://www.binarylogic.com/2008/11/3/tutorial-authlogic-basic-setup
74
+ * <b>Authlogic reset passwords tutorial:</b> http://www.binarylogic.com/2008/11/16/tutorial-reset-passwords-with-authlogic
74
75
  * <b>Live example of the setup tutorial above (with source):</b> http://authlogic_example.binarylogic.com
75
76
  * <b>Bugs / feature suggestions:</b> http://binarylogic.lighthouseapp.com/projects/18752-authlogic
76
77
 
@@ -105,11 +106,13 @@ Create your user_session.rb file:
105
106
 
106
107
  The user model needs to have the following columns. The names of these columns can be changed with configuration. Better yet, Authlogic tries to guess these names by checking for the existence of common names. See Authlogic::Session::Config::ClassMethods for more details, but chances are you won't have to specify any configuration for your field names, even if they aren't the same names as below.
107
108
 
108
- t.string :login, :null => false
109
- t.string :crypted_password, :null => false
110
- t.string :password_salt, :null => false # not needed if you are encrypting your pw instead of using a hash algorithm
111
- t.string :remember_token, :null => false
112
- t.integer :login_count # This is optional, it is a "magic" column, just like "created_at". See below for a list of all magic columns.
109
+ t.string :login, :null => false
110
+ t.string :crypted_password, :null => false
111
+ t.string :password_salt, :null => false # not needed if you are encrypting your pw instead of using a hash algorithm.
112
+ t.string :remember_token, :null => false
113
+ t.string :single_access_token, :null => false # optional, see the single access section below.
114
+ t.string :password_reset_token, :null => false # optional, see the password reset section below.
115
+ t.integer :login_count # optional, this is a "magic" column, see the magic columns section below
113
116
 
114
117
  === Set up your model
115
118
 
@@ -119,7 +122,7 @@ Make sure you have a model that you will be authenticating with. For this exampl
119
122
  acts_as_authentic # for options see documentation: Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Config
120
123
  end
121
124
 
122
- Done! Now go use it just like you would with any other ActiveRecord model. Either glance at the code at the beginning of this readme or check out the tutorial (see above in "helpful links") for a more detailed walk through.
125
+ Done! Now go use it just like you would with any other ActiveRecord model. Either glance at the code at the beginning of this README or check out the tutorials (see above in "helpful links") for a more detailed walk through.
123
126
 
124
127
  == Magic Columns
125
128
 
@@ -148,19 +151,23 @@ Need Authlogic to check your own "state"? No problem, check out the hooks sectio
148
151
 
149
152
  == Hooks / Callbacks
150
153
 
151
- Just like ActiveRecord you can create your own hooks / callbacks so that you can do whatever you want when certain actions are performed. Here they are:
154
+ Just like ActiveRecord you can create your own hooks / callbacks so that you can do whatever you want when certain actions are performed. Such as before_save, after_save, etc.
152
155
 
153
156
  before_create
154
157
  after_create
155
158
  before_destroy
156
159
  after_destroy
160
+ before_find
161
+ after_find
157
162
  before_save
158
163
  after_save
159
164
  before_update
160
165
  after_update
161
166
  before_validation
162
167
  after_validation
163
-
168
+
169
+ See Authlogic::Session::Callbacks for more information
170
+
164
171
  == Multiple Sessions / Session Identifiers
165
172
 
166
173
  You're asking: "why would I want multiple sessions?". Take this example:
@@ -171,7 +178,7 @@ You have an app where users login and then need to re-login to view / change the
171
178
  @user_session = UserSession.new
172
179
  @user_session.id
173
180
  # => nil
174
-
181
+
175
182
  # secure user session
176
183
  @secure_user_session = UserSession.new(:secure)
177
184
  @secure_user_session.id
@@ -182,25 +189,39 @@ This will keep everything separate. The :secure session will store its info in a
182
189
  @user_session = UserSession.find
183
190
  @secure_user_session = UserSession.find(:secure)
184
191
 
185
- For more information on ids checkout Authlogic::Session::Base#initialize
192
+ For more information on ids checkout Authlogic::Session::Base#id
193
+
194
+ == Resetting passwords
195
+
196
+ You may have noticed in the helpful links section is a tutorial on resetting password with Authlogic. I'm not going to repeat myself here, but I will touch on the basics, if you want more information please see the tutorial.
197
+
198
+ Just add the following field to your database:
199
+
200
+ t.string :password_reset_token, :null => false
201
+
202
+ Authlogic will notice this field and take care of maintaining it for you. You should use the value of this field to verify your user before they reset their password. There is a finder method you can use to find users with this token, I highly recommend using this method, as it adds in extra security checks to verify the user. See Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::PasswordReset for more information.
186
203
 
187
204
  == Single Access / Private Feeds Access
188
205
 
189
- Need to provide a single / one time access to an account where the session does NOT get persisted? Take a private feed for example, if everyone followed standards basic http auth should work just fine, but since we live in a world where following standards is a hard concept (*cough* Microsoft *cough*), the feed url needs to have some sort of "credentials" to log the user in and get their user specific feed items. This is easy, Authlogic has a nifty little feature for doing just this. All that you need to do is add the following field in your table:
206
+ Need to provide a single / one time access to an account where the session does NOT get persisted? Take a private feed for example, if everyone followed standards, basic http auth should work just fine, but since we live in a world where following standards is not a standard (\*cough\* Microsoft \*cough\*), the feed url needs to have some sort of "credentials" to log the user in and get their user specific feed items. This is easy, Authlogic has a nifty little feature for doing just this. All that you need to do is add the following field in your table:
207
+
208
+ t.string :single_access_token, :null => false
209
+ # or call it feeds_token, feed_token, or whatever you want with configuration
190
210
 
191
- t.string :single_access_token, :null => false # or call it feeds_token or feed_token
211
+ Authlogic will notice you have this and adjust accordingly. By default single_access_tokens can only be used to login for rss and atom request types.
192
212
 
193
- Authlogic will notice you have this and adjust accordingly. You have the follow configuration options for your session (Authlogic::Session::Config) to customize how this works:
213
+ To tailor how this works, you have the following configuration options:
194
214
 
195
- 1. <tt>params_key:</tt> params_key is the key Authlogic will look for when trying to find your session. It works just like your cookie and session key, except this is for params. Take a UserSession: http://www.mydomin.com?user_credentials=single_access_token
196
- 2. <tt>single_access_allowed_request_types:</tt> Single access needs to be handled with care, after all, it gives the user access to their account. But maybe you don't want to allow this for your entire application. Maybe you only want to allow this for certain request types, such as application/rss+xml or application/atom+xml. By default single access is only allowed for these requests types.
197
- 3. <tt>single_access_token_field:</tt> This works just like remember_token_field. It basically allows you to name the column that the single_access_token is stored in.
198
- 4. change_single_access_token_with_password
215
+ Session configuration (Authlogic::Session::Config)
199
216
 
200
- You also have the following options when calling acts_as_authentic (Authlogic::ORMAdapters::ActiveRecordAdapter::Config):
217
+ 1. params_key
218
+ 2. single_access_allowed_request_types
219
+ 3. single_access_token_field
201
220
 
202
- 1. <tt>single_access_token_field:</tt> Works the same as remember_token field.
203
- 2. <tt>change_single_access_token_with_password:</tt> If the user changes their password do you want to change the single access token as well? This will require that they re-add the feed with the new token, as their old URL will not longer work. It's really up to you if you want to do this. The other alternative is to provide an option when they are changing their password to change their "feed token" as well. You can call user.reset_single_access_token to do this yourself.
221
+ Model configuration (Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Config)
222
+
223
+ 1. single_access_token_field:
224
+ 2. change_single_access_token_with_password
204
225
 
205
226
  Please use this with care and make sure you warn your users that the URL you provide them is to remain private. Even if Billy 13 year old gets this URL and tries to log in, the only way he can login is through a GET or POST parameter with an rss or atom request. Billy can't create a cookie with this token and Billy wont have access to anything else on the site, unless you change the above configuration.
206
227
 
@@ -253,7 +274,7 @@ What's neat about cookies is that if you use sub domains they automatically scop
253
274
 
254
275
  But what if you *don't* want to separate your cookies by subdomains? You can accomplish this by doing:
255
276
 
256
- ActionController::Base.session_options[:session_domain] = ‘.mydomain.com
277
+ ActionController::Base.session_options[:session_domain] = '.mydomain.com'
257
278
 
258
279
  If for some reason the above doesn't work for you, do some simple Google searches. There are a million blog posts on this.
259
280
 
@@ -300,8 +321,9 @@ Here is basically how this is done....
300
321
 
301
322
  private
302
323
  def maintain_sessions!
303
- # If we aren't logged in at all and the password was changed, go ahead and log the user in
304
- # If we are logged in and the password has change, update the sessions
324
+ # If we aren't logged in and a user is create log them in as that user
325
+ # If we aren't logged in and a user's password changes, log them in as that user
326
+ # If we are logged in and they change their password, update the session so they remain logged in
305
327
  end
306
328
  end
307
329
 
@@ -339,6 +361,10 @@ Using a library that hundreds of other people use has it advantages. Probably on
339
361
 
340
362
  Lastly, there is a pattern here, why clutter up all of your applications with the same code over and over?
341
363
 
364
+ === Why test the same code over and over?
365
+
366
+ I've noticed my apps get cluttered with authentication tests, and they are the same exact tests! This irritates me. When you have identical tests across your apps thats a red flag that code can be extracted into a library. What's great about Authlogic is that I tested it for you. You don't write tests that test the internals of ActiveRecord do you? The same applies for Authlogic. Only test code that you've written. Essentially testing authentication is similar to testing any another RESTful controller. This makes your tests focused and easier to understand.
367
+
342
368
  === Limited to a single authentication
343
369
 
344
370
  I recently had an app where you could log in as a user and also log in as an employee. I won't go into the specifics of the app, but it made the most sense to do it this way. So I had two sessions in one app. None of the current solutions I found easily supported this. They all assumed a single session. One session was messy enough, adding another just put me over the edge and eventually forced me to write Authlogic. Authlogic can support 100 different sessions easily and in a clean format. Just like an app can support 100 different models and 100 different records of each model.
@@ -1,14 +1,14 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{authlogic}
3
- s.version = "1.1.2"
3
+ s.version = "1.2.0"
4
4
 
5
5
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
6
6
  s.authors = ["Ben Johnson of Binary Logic"]
7
- s.date = %q{2008-11-13}
7
+ s.date = %q{2008-11-16}
8
8
  s.description = %q{A clean, simple, and unobtrusive ruby authentication solution.}
9
9
  s.email = %q{bjohnson@binarylogic.com}
10
- s.extra_rdoc_files = ["CHANGELOG.rdoc", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/merb_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/crypto_providers/sha1.rb", "lib/authlogic/crypto_providers/sha512.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb", "lib/authlogic/orm_adapters/active_record_adapter/authenticates_many.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/authenticates_many_association.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/cookies.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/session/params.rb", "lib/authlogic/session/scopes.rb", "lib/authlogic/session/session.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "README.rdoc"]
11
- s.files = ["CHANGELOG.rdoc", "init.rb", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/merb_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/crypto_providers/sha1.rb", "lib/authlogic/crypto_providers/sha512.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb", "lib/authlogic/orm_adapters/active_record_adapter/authenticates_many.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/authenticates_many_association.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/cookies.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/session/params.rb", "lib/authlogic/session/scopes.rb", "lib/authlogic/session/session.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "Manifest", "MIT-LICENSE", "Rakefile", "README.rdoc", "shoulda_macros/authlogic.rb", "test/fixtures/companies.yml", "test/fixtures/employees.yml", "test/fixtures/projects.yml", "test/fixtures/users.yml", "test/libs/aes128_crypto_provider.rb", "test/libs/mock_controller.rb", "test/libs/mock_cookie_jar.rb", "test/libs/mock_request.rb", "test/libs/ordered_hash.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/single_access_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/authenticates_many_test.rb", "test/session_tests/active_record_trickery_test.rb", "test/session_tests/authenticates_many_association_test.rb", "test/session_tests/base_test.rb", "test/session_tests/config_test.rb", "test/session_tests/cookies_test.rb", "test/session_tests/params_test.rb", "test/session_tests/scopes_test.rb", "test/session_tests/session_test.rb", "test/test_helper.rb", "authlogic.gemspec"]
10
+ s.extra_rdoc_files = ["CHANGELOG.rdoc", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/merb_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/crypto_providers/sha1.rb", "lib/authlogic/crypto_providers/sha512.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb", "lib/authlogic/orm_adapters/active_record_adapter/authenticates_many.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/authenticates_many_association.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/cookies.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/session/params.rb", "lib/authlogic/session/password_reset.rb", "lib/authlogic/session/scopes.rb", "lib/authlogic/session/session.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "README.rdoc"]
11
+ s.files = ["CHANGELOG.rdoc", "init.rb", "lib/authlogic/controller_adapters/abstract_adapter.rb", "lib/authlogic/controller_adapters/merb_adapter.rb", "lib/authlogic/controller_adapters/rails_adapter.rb", "lib/authlogic/crypto_providers/sha1.rb", "lib/authlogic/crypto_providers/sha512.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb", "lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb", "lib/authlogic/orm_adapters/active_record_adapter/authenticates_many.rb", "lib/authlogic/session/active_record_trickery.rb", "lib/authlogic/session/authenticates_many_association.rb", "lib/authlogic/session/base.rb", "lib/authlogic/session/callbacks.rb", "lib/authlogic/session/config.rb", "lib/authlogic/session/cookies.rb", "lib/authlogic/session/errors.rb", "lib/authlogic/session/params.rb", "lib/authlogic/session/password_reset.rb", "lib/authlogic/session/scopes.rb", "lib/authlogic/session/session.rb", "lib/authlogic/version.rb", "lib/authlogic.rb", "Manifest", "MIT-LICENSE", "Rakefile", "README.rdoc", "shoulda_macros/authlogic.rb", "test/fixtures/companies.yml", "test/fixtures/employees.yml", "test/fixtures/projects.yml", "test/fixtures/users.yml", "test/libs/aes128_crypto_provider.rb", "test/libs/mock_controller.rb", "test/libs/mock_cookie_jar.rb", "test/libs/mock_request.rb", "test/libs/ordered_hash.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/password_reset_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/single_access_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/authenticates_many_test.rb", "test/session_tests/active_record_trickery_test.rb", "test/session_tests/authenticates_many_association_test.rb", "test/session_tests/base_test.rb", "test/session_tests/config_test.rb", "test/session_tests/cookies_test.rb", "test/session_tests/params_test.rb", "test/session_tests/password_reset_test.rb", "test/session_tests/scopes_test.rb", "test/session_tests/session_test.rb", "test/test_helper.rb", "authlogic.gemspec"]
12
12
  s.has_rdoc = true
13
13
  s.homepage = %q{http://github.com/binarylogic/authlogic}
14
14
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Authlogic", "--main", "README.rdoc"]
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.rubyforge_project = %q{authlogic}
17
17
  s.rubygems_version = %q{1.2.0}
18
18
  s.summary = %q{A clean, simple, and unobtrusive ruby authentication solution.}
19
- s.test_files = ["test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/single_access_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/authenticates_many_test.rb", "test/session_tests/active_record_trickery_test.rb", "test/session_tests/authenticates_many_association_test.rb", "test/session_tests/base_test.rb", "test/session_tests/config_test.rb", "test/session_tests/cookies_test.rb", "test/session_tests/params_test.rb", "test/session_tests/scopes_test.rb", "test/session_tests/session_test.rb", "test/test_helper.rb"]
19
+ s.test_files = ["test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/password_reset_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/single_access_test.rb", "test/orm_adapters_tests/active_record_adapter_tests/authenticates_many_test.rb", "test/session_tests/active_record_trickery_test.rb", "test/session_tests/authenticates_many_association_test.rb", "test/session_tests/base_test.rb", "test/session_tests/config_test.rb", "test/session_tests/cookies_test.rb", "test/session_tests/params_test.rb", "test/session_tests/password_reset_test.rb", "test/session_tests/scopes_test.rb", "test/session_tests/session_test.rb", "test/test_helper.rb"]
20
20
 
21
21
  if s.respond_to? :specification_version then
22
22
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -13,6 +13,7 @@ if defined?(ActiveRecord)
13
13
  require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic"
14
14
  require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials"
15
15
  require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in"
16
+ require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset"
16
17
  require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence"
17
18
  require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance"
18
19
  require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access"
@@ -27,6 +28,7 @@ require File.dirname(__FILE__) + "/authlogic/session/config"
27
28
  require File.dirname(__FILE__) + "/authlogic/session/cookies"
28
29
  require File.dirname(__FILE__) + "/authlogic/session/errors"
29
30
  require File.dirname(__FILE__) + "/authlogic/session/params"
31
+ require File.dirname(__FILE__) + "/authlogic/session/password_reset"
30
32
  require File.dirname(__FILE__) + "/authlogic/session/session"
31
33
  require File.dirname(__FILE__) + "/authlogic/session/scopes"
32
34
  require File.dirname(__FILE__) + "/authlogic/session/base"
@@ -38,6 +40,7 @@ module Authlogic
38
40
  include Callbacks
39
41
  include Cookies
40
42
  include Params
43
+ include PasswordReset
41
44
  include Session
42
45
  include Scopes
43
46
  end
@@ -34,20 +34,27 @@ module Authlogic
34
34
  #
35
35
  # * <tt>login_field_regex_failed_message</tt> - the message to use when the validates_format_of for the login field fails. This depends on if you are
36
36
  # performing :email or :login regex.
37
+ #
38
+ # * <tt>email_field</tt> - default: :email, depending on if it is present, if :email is not present defaults to nil
39
+ # The name of the field used to store the email address. Only specify this if you arent using this as your :login_field.
40
+ #
41
+ # * <tt>email_field_regex</tt> - default: type email regex
42
+ # This is used in validates_format_of for the :email_field.
43
+ #
44
+ # * <tt>email_field_regex_failed_message</tt> - the message to use when the validates_format_of for the email field fails.
37
45
  #
38
46
  # * <tt>change_single_access_token_with_password</tt> - default: false,
39
47
  # When a user changes their password do you want the single access token to change as well? That's what this configuration option is all about.
40
48
  #
41
- # * <tt>single_access_token_field</tt> - default: :single_access_token, :feed_token, or :feeds_token, depending on which column is present,
49
+ # * <tt>single_access_token_field</tt> - default: :single_access_token, :feed_token, or :feeds_token, depending on which column is present, if none are present defaults to nil
42
50
  # This is the name of the field to login with single access, mainly used for private feed access. Only specify if the name of the field is different
43
51
  # then the defaults. See the "Single Access" section in the README for more details on how single access works.
44
52
  #
45
53
  # * <tt>password_field</tt> - default: :password,
46
54
  # This is the name of the field to set the password, *NOT* the field the encrypted password is stored. Defaults the what the configuration
47
55
  #
48
- # * <tt>crypted_password_field</tt> - default: depends on which columns are present,
49
- # The name of the database field where your encrypted password is stored. If the name of the field is different from any of the following
50
- # you need to specify it with this option: crypted_password, encrypted_password, password_hash, pw_hash
56
+ # * <tt>crypted_password_field</tt> - default: :crypted_password, :encrypted_password, :password_hash, :pw_hash, depends on which columns are present, if none are present defaults to nil
57
+ # The name of the database field where your encrypted password is stored.
51
58
  #
52
59
  # * <tt>password_blank_message</tt> - default: "can not be blank",
53
60
  # The error message used when the password is left blank.
@@ -57,6 +64,15 @@ module Authlogic
57
64
  #
58
65
  # * <tt>password_salt_field</tt> - default: :password_salt, :pw_salt, or :salt, depending on which column is present, defaults to :password_salt if none are present,
59
66
  # This is the name of the field in your database that stores your password salt.
67
+ #
68
+ # * <tt>password_reset_token_field</tt> - default: :password_reset_token, :pw_reset_token, :reset_password_token, or :reset_pw_token, depending on which column is present, if none are present defaults to nil
69
+ # This is the name of the field in your database that stores your password reset token. The token you should use to verify your users before you allow a password reset. Authlogic takes care
70
+ # of maintaining this for you and making sure it changes when needed.
71
+ #
72
+ # * <tt>password_reset_token_valid_for</tt> - default: 10.minutes,
73
+ # Authlogic gives you a sepcial method for finding records by the password reset token (see Authlogic::ORMAdapters::ActiveRecordAdapter::ActcsAsAuthentic::PasswordReset). In this method
74
+ # it checks for the age of the token. If the token is old than whatever you specify here, a user will NOT be returned. This way the tokens are perishable, thus making this system much
75
+ # more secure.
60
76
  #
61
77
  # * <tt>remember_token_field</tt> - default: :remember_token, :remember_key, :cookie_tokien, or :cookie_key, depending on which column is present, defaults to :remember_token if none are present,
62
78
  # This is the name of the field your remember_token is stored. The remember token is a unique token that is stored in the users cookie and
@@ -93,14 +109,19 @@ module Authlogic
93
109
  options[:crypto_provider] ||= CryptoProviders::Sha512
94
110
  options[:login_field] ||= first_column_to_exist(:login, :username, :email)
95
111
  options[:login_field_type] ||= options[:login_field] == :email ? :email : :login
96
-
112
+ options[:email_field] = first_column_to_exist(nil, :email) unless options.key?(:email_field)
113
+ options[:email_field] = nil if options[:email_field] == options[:login_field]
114
+
115
+ email_name_regex = '[\w\.%\+\-]+'
116
+ domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
117
+ domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'
118
+ options[:email_field_regex] ||= /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
119
+ options[:email_field_regex_failed_message] ||= "should look like an email address."
120
+
97
121
  case options[:login_field_type]
98
122
  when :email
99
- email_name_regex = '[\w\.%\+\-]+'
100
- domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
101
- domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'
102
- options[:login_field_regex] ||= /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
103
- options[:login_field_regex_failed_message] ||= "should look like an email address."
123
+ options[:login_field_regex] ||= options[:email_field_regex]
124
+ options[:login_field_regex_failed_message] ||= options[:email_field_regex_failed_message]
104
125
  else
105
126
  options[:login_field_regex] ||= /\A\w[\w\.\-_@ ]+\z/
106
127
  options[:login_field_regex_failed_message] ||= "use only letters, numbers, spaces, and .-_@ please."
@@ -113,6 +134,9 @@ module Authlogic
113
134
  options[:password_salt_field] ||= first_column_to_exist(:password_salt, :pw_salt, :salt)
114
135
  options[:remember_token_field] ||= first_column_to_exist(:remember_token, :remember_key, :cookie_token, :cookiey_key)
115
136
  options[:single_access_token_field] ||= first_column_to_exist(nil, :single_access_token, :feed_token, :feeds_token)
137
+ options[:password_reset_token_field] ||= first_column_to_exist(nil, :password_reset_token, :pw_reset_token, :reset_password_token, :reset_pw_token)
138
+ options[:password_reset_token_valid_for] ||= 10.minutes
139
+ options[:password_reset_token_valid_for] = options[:password_reset_token_valid_for].to_i
116
140
  options[:logged_in_timeout] ||= 10.minutes
117
141
  options[:logged_in_timeout] = options[:logged_in_timeout].to_i
118
142
  options[:session_ids] ||= [nil]
@@ -13,7 +13,7 @@ module Authlogic
13
13
  # === Instance Methods
14
14
  #
15
15
  # * <tt>{options[:password_field]}=(value)</tt> - encrypts a raw password and sets it to your crypted_password_field. Also sets the password_salt to a random token.
16
- # * <tt>valid_{options[:password_field]}?(password_to_check)</tt> - checks is the password is valid. The password passed can be the raw password or the encrypted password.
16
+ # * <tt>valid_{options[:password_field]}?(password_to_check)</tt> - checks is the password is valid. The password passed must be the raw password, not encrypted.
17
17
  # * <tt>reset_{options[:password_field]}</tt> - resets the password using the friendly_unique_token class method
18
18
  # * <tt>reset_{options[:password_field]}!</tt> - calls reset_password and then saves the record
19
19
  module Credentials
@@ -29,6 +29,12 @@ module Authlogic
29
29
  validates_length_of options[:login_field], :within => 2..100, :allow_blank => true
30
30
  validates_format_of options[:login_field], :with => options[:login_field_regex], :message => options[:login_field_regex_failed_message]
31
31
  end
32
+
33
+ if options[:email_field]
34
+ validates_length_of options[:email_field], :within => 6..100
35
+ validates_format_of options[:email_field], :with => options[:email_field_regex], :message => options[:email_field_regex_failed_message]
36
+ validates_uniqueness_of options[:email_field], :scope => options[:scope]
37
+ end
32
38
 
33
39
  validates_uniqueness_of options[:login_field], :scope => options[:scope]
34
40
  validate :validate_password
@@ -54,8 +60,7 @@ module Authlogic
54
60
 
55
61
  def valid_#{options[:password_field]}?(attempted_password)
56
62
  return false if attempted_password.blank? || #{options[:crypted_password_field]}.blank? || #{options[:password_salt_field]}.blank?
57
- attempted_password == #{options[:crypted_password_field]} ||
58
- (#{options[:crypto_provider]}.respond_to?(:decrypt) && #{options[:crypto_provider]}.decrypt(#{options[:crypted_password_field]}) == attempted_password + #{options[:password_salt_field]}) ||
63
+ (#{options[:crypto_provider]}.respond_to?(:decrypt) && #{options[:crypto_provider]}.decrypt(#{options[:crypted_password_field]}) == attempted_password + #{options[:password_salt_field]}) ||
59
64
  (!#{options[:crypto_provider]}.respond_to?(:decrypt) && #{options[:crypto_provider]}.encrypt(attempted_password + #{options[:password_salt_field]}) == #{options[:crypted_password_field]})
60
65
  end
61
66
 
@@ -22,8 +22,8 @@ module Authlogic
22
22
  validates_numericality_of :login_count, :only_integer => :true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("login_count")
23
23
 
24
24
  if column_names.include?("last_request_at")
25
- named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", options[:logged_in_timeout].ago]} }
26
- named_scope :logged_out, lambda { {:conditions => ["last_request_at is NULL or last_request_at <= ?", options[:logged_in_timeout].ago]} }
25
+ named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", options[:logged_in_timeout].seconds.ago]} }
26
+ named_scope :logged_out, lambda { {:conditions => ["last_request_at is NULL or last_request_at <= ?", options[:logged_in_timeout].seconds.ago]} }
27
27
  end
28
28
 
29
29
  if column_names.include?("last_request_at")
@@ -0,0 +1,73 @@
1
+ module Authlogic
2
+ module ORMAdapters
3
+ module ActiveRecordAdapter
4
+ module ActsAsAuthentic
5
+ # = Password Reset
6
+ #
7
+ # Handles all logic the deals with maintaining the password reset token. This token should be used to authenticate a user that is not logged in so that they
8
+ # can change their password.
9
+ #
10
+ # === Class Methods
11
+ #
12
+ # * <tt>find_using_{options[:password_reset_token_field]}(token)</tt> - returns the record that matches the pased token. The record's updated at column must not be older than
13
+ # {options[:password_reset_token_valid_for]} ago. Lastly, if a blank token is passed no record will be returned.
14
+ #
15
+ # === Instance Methods
16
+ #
17
+ # * <tt>reset_#{options[:password_reset_token_field]}</tt> - resets the password reset token field to a friendly unique token.
18
+ # * <tt>reset_#{options[:password_reset_token_field]}!</tt> - same as above but saves the record afterwards.
19
+ module PasswordReset
20
+ def acts_as_authentic_with_password_reset(options = {})
21
+ acts_as_authentic_without_password_reset(options)
22
+
23
+ return if options[:password_reset_token_field].blank?
24
+
25
+ class_eval <<-"end_eval", __FILE__, __LINE__
26
+ validates_uniqueness_of :#{options[:password_reset_token_field]}
27
+
28
+ before_validation :reset_#{options[:password_reset_token_field]}, :unless => :resetting_#{options[:password_reset_token_field]}?
29
+
30
+ def self.find_using_#{options[:password_reset_token_field]}(token)
31
+ return if token.blank?
32
+
33
+ conditions_sql = "#{options[:password_reset_token_field]} = ?"
34
+ conditions_subs = [token]
35
+
36
+ if column_names.include?("updated_at") && #{options[:password_reset_token_valid_for]} > 0
37
+ conditions_sql += " and updated_at > ?"
38
+ conditions_subs << #{options[:password_reset_token_valid_for]}.seconds.ago
39
+ end
40
+
41
+ find(:first, :conditions => [conditions_sql, *conditions_subs])
42
+ end
43
+
44
+ def reset_#{options[:password_reset_token_field]}
45
+ self.#{options[:password_reset_token_field]} = self.class.friendly_unique_token
46
+ end
47
+
48
+ def reset_#{options[:password_reset_token_field]}!
49
+ reset_#{options[:password_reset_token_field]}
50
+ @resetting_#{options[:password_reset_token_field]} = true
51
+ result = save_without_session_maintenance
52
+ @resetting_#{options[:password_reset_token_field]} = false
53
+ result
54
+ end
55
+
56
+ private
57
+ def resetting_#{options[:password_reset_token_field]}?
58
+ @resetting_#{options[:password_reset_token_field]} == true
59
+ end
60
+ end_eval
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ ActiveRecord::Base.class_eval do
69
+ class << self
70
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::PasswordReset
71
+ alias_method_chain :acts_as_authentic, :password_reset
72
+ end
73
+ end