Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Release v1.2.0

  • Loading branch information...
commit dbd8b8f57b57536fbdf8e7ae58b4da2f22833e7b 1 parent c190c77
@binarylogic binarylogic authored
Showing with 571 additions and 172 deletions.
  1. +7 −1 CHANGELOG.rdoc
  2. +4 −0 Manifest
  3. +37 −19 README.rdoc
  4. +3 −0  lib/authlogic.rb
  5. +34 −10 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb
  6. +8 −3 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb
  7. +73 −0 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset.rb
  8. +27 −27 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb
  9. +41 −38 lib/authlogic/session/base.rb
  10. +125 −32 lib/authlogic/session/config.rb
  11. +17 −0 lib/authlogic/session/password_reset.rb
  12. +2 −6 lib/authlogic/session/scopes.rb
  13. +2 −2 lib/authlogic/version.rb
  14. +3 −0  test/fixtures/users.yml
  15. +6 −1 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb
  16. +16 −2 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb
  17. +40 −0 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/password_reset_test.rb
  18. +1 −1  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb
  19. +9 −8 test/session_tests/base_test.rb
  20. +84 −12 test/session_tests/config_test.rb
  21. +15 −0 test/session_tests/password_reset_test.rb
  22. +5 −4 test/session_tests/scopes_test.rb
  23. +12 −6 test/test_helper.rb
View
8 CHANGELOG.rdoc
@@ -1,7 +1,13 @@
-== 1.1.2 released 2008-11-13
+== 1.2.0 released 2008-11-16
* Added check for database set up in acts_as_authentic to prevent errors during migrations.
* Forced logged_in and logged_out named scopes to use seconds.
+* Hardened valid_password? method to only allow raw passwords.
+* 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.
+* Removed single_access_token_field and remember_token_field from Sesson::Config, they are not needed there.
+* Added password_reset_token to assist in resetting passwords.
+* 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.
+* Added configuration for all validation messages for the session so that you can modify them and provide I18n support.
== 1.1.1 released 2008-11-13
View
4 Manifest
@@ -8,6 +8,7 @@ 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
@@ -21,6 +22,7 @@ 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
@@ -42,6 +44,7 @@ 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
@@ -52,6 +55,7 @@ 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
View
56 README.rdoc
@@ -53,7 +53,7 @@ Or how about persisting the session...
class ApplicationController
helper_method :current_user_session, :current_user
- protected
+ private
def current_user_session
return @current_user_session if defined?(@current_user_session)
@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
* <b>Documentation:</b> http://authlogic.rubyforge.org
* <b>Authlogic setup tutorial:</b> http://www.binarylogic.com/2008/11/3/tutorial-authlogic-basic-setup
+* <b>Authlogic reset passwords tutorial:</b> http://www.binarylogic.com/2008/11/16/tutorial-reset-passwords-with-authlogic
* <b>Live example of the setup tutorial above (with source):</b> http://authlogic_example.binarylogic.com
* <b>Bugs / feature suggestions:</b> http://binarylogic.lighthouseapp.com/projects/18752-authlogic
@@ -105,11 +106,13 @@ Create your user_session.rb file:
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.
- t.string :login, :null => false
- t.string :crypted_password, :null => false
- t.string :password_salt, :null => false # not needed if you are encrypting your pw instead of using a hash algorithm
- t.string :remember_token, :null => false
- 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.
+ t.string :login, :null => false
+ t.string :crypted_password, :null => false
+ t.string :password_salt, :null => false # not needed if you are encrypting your pw instead of using a hash algorithm.
+ t.string :remember_token, :null => false
+ t.string :single_access_token, :null => false # optional, see the single access section below.
+ t.string :password_reset_token, :null => false # optional, see the password reset section below.
+ t.integer :login_count # optional, this is a "magic" column, see the magic columns section below
=== Set up your model
@@ -119,7 +122,7 @@ Make sure you have a model that you will be authenticating with. For this exampl
acts_as_authentic # for options see documentation: Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Config
end
-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.
+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.
== Magic Columns
@@ -148,7 +151,7 @@ Need Authlogic to check your own "state"? No problem, check out the hooks sectio
== Hooks / Callbacks
-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:
+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.
before_create
after_create
@@ -162,7 +165,9 @@ Just like ActiveRecord you can create your own hooks / callbacks so that you can
after_update
before_validation
after_validation
-
+
+See Authlogic::Session::Callbacks for more information
+
== Multiple Sessions / Session Identifiers
You're asking: "why would I want multiple sessions?". Take this example:
@@ -173,7 +178,7 @@ You have an app where users login and then need to re-login to view / change the
@user_session = UserSession.new
@user_session.id
# => nil
-
+
# secure user session
@secure_user_session = UserSession.new(:secure)
@secure_user_session.id
@@ -186,6 +191,16 @@ This will keep everything separate. The :secure session will store its info in a
For more information on ids checkout Authlogic::Session::Base#id
+== Resetting passwords
+
+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.
+
+Just add the following field to your database:
+
+t.string :password_reset_token, :null => false
+
+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.
+
== Single Access / Private Feeds Access
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:
@@ -193,17 +208,20 @@ Need to provide a single / one time access to an account where the session does
t.string :single_access_token, :null => false
# or call it feeds_token, feed_token, or whatever you want with configuration
-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:
+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.
+
+To tailor how this works, you have the following configuration options:
+
+Session configuration (Authlogic::Session::Config)
-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
-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.
-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.
-4. change_single_access_token_with_password
+1. params_key
+2. single_access_allowed_request_types
+3. single_access_token_field
-You also have the following options when calling acts_as_authentic (Authlogic::ORMAdapters::ActiveRecordAdapter::Config):
+Model configuration (Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Config)
-1. <tt>single_access_token_field:</tt> Works the same as remember_token field.
-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.
+1. single_access_token_field:
+2. change_single_access_token_with_password
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.
@@ -343,7 +361,7 @@ Using a library that hundreds of other people use has it advantages. Probably on
Lastly, there is a pattern here, why clutter up all of your applications with the same code over and over?
-=== Why test the same code over and over
+=== Why test the same code over and over?
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.
View
3  lib/authlogic.rb
@@ -13,6 +13,7 @@
require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic"
require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials"
require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in"
+ require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset"
require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence"
require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance"
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/cookies"
require File.dirname(__FILE__) + "/authlogic/session/errors"
require File.dirname(__FILE__) + "/authlogic/session/params"
+require File.dirname(__FILE__) + "/authlogic/session/password_reset"
require File.dirname(__FILE__) + "/authlogic/session/session"
require File.dirname(__FILE__) + "/authlogic/session/scopes"
require File.dirname(__FILE__) + "/authlogic/session/base"
@@ -38,6 +40,7 @@ class Base
include Callbacks
include Cookies
include Params
+ include PasswordReset
include Session
include Scopes
end
View
44 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb
@@ -34,20 +34,27 @@ module ActsAsAuthentic
#
# * <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
# performing :email or :login regex.
+ #
+ # * <tt>email_field</tt> - default: :email, depending on if it is present, if :email is not present defaults to nil
+ # The name of the field used to store the email address. Only specify this if you arent using this as your :login_field.
+ #
+ # * <tt>email_field_regex</tt> - default: type email regex
+ # This is used in validates_format_of for the :email_field.
+ #
+ # * <tt>email_field_regex_failed_message</tt> - the message to use when the validates_format_of for the email field fails.
#
# * <tt>change_single_access_token_with_password</tt> - default: false,
# 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.
#
- # * <tt>single_access_token_field</tt> - default: :single_access_token, :feed_token, or :feeds_token, depending on which column is present,
+ # * <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
# 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
# then the defaults. See the "Single Access" section in the README for more details on how single access works.
#
# * <tt>password_field</tt> - default: :password,
# This is the name of the field to set the password, *NOT* the field the encrypted password is stored. Defaults the what the configuration
#
- # * <tt>crypted_password_field</tt> - default: depends on which columns are present,
- # 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
- # you need to specify it with this option: crypted_password, encrypted_password, password_hash, pw_hash
+ # * <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
+ # The name of the database field where your encrypted password is stored.
#
# * <tt>password_blank_message</tt> - default: "can not be blank",
# The error message used when the password is left blank.
@@ -57,6 +64,15 @@ module ActsAsAuthentic
#
# * <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,
# This is the name of the field in your database that stores your password salt.
+ #
+ # * <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
+ # 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
+ # of maintaining this for you and making sure it changes when needed.
+ #
+ # * <tt>password_reset_token_valid_for</tt> - default: 10.minutes,
+ # Authlogic gives you a sepcial method for finding records by the password reset token (see Authlogic::ORMAdapters::ActiveRecordAdapter::ActcsAsAuthentic::PasswordReset). In this method
+ # 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
+ # more secure.
#
# * <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,
# 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 @@ def acts_as_authentic_with_config(options = {})
options[:crypto_provider] ||= CryptoProviders::Sha512
options[:login_field] ||= first_column_to_exist(:login, :username, :email)
options[:login_field_type] ||= options[:login_field] == :email ? :email : :login
-
+ options[:email_field] = first_column_to_exist(nil, :email) unless options.key?(:email_field)
+ options[:email_field] = nil if options[:email_field] == options[:login_field]
+
+ email_name_regex = '[\w\.%\+\-]+'
+ domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
+ domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'
+ options[:email_field_regex] ||= /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
+ options[:email_field_regex_failed_message] ||= "should look like an email address."
+
case options[:login_field_type]
when :email
- email_name_regex = '[\w\.%\+\-]+'
- domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
- domain_tld_regex = '(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)'
- options[:login_field_regex] ||= /\A#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}\z/i
- options[:login_field_regex_failed_message] ||= "should look like an email address."
+ options[:login_field_regex] ||= options[:email_field_regex]
+ options[:login_field_regex_failed_message] ||= options[:email_field_regex_failed_message]
else
options[:login_field_regex] ||= /\A\w[\w\.\-_@ ]+\z/
options[:login_field_regex_failed_message] ||= "use only letters, numbers, spaces, and .-_@ please."
@@ -113,6 +134,9 @@ def acts_as_authentic_with_config(options = {})
options[:password_salt_field] ||= first_column_to_exist(:password_salt, :pw_salt, :salt)
options[:remember_token_field] ||= first_column_to_exist(:remember_token, :remember_key, :cookie_token, :cookiey_key)
options[:single_access_token_field] ||= first_column_to_exist(nil, :single_access_token, :feed_token, :feeds_token)
+ options[:password_reset_token_field] ||= first_column_to_exist(nil, :password_reset_token, :pw_reset_token, :reset_password_token, :reset_pw_token)
+ options[:password_reset_token_valid_for] ||= 10.minutes
+ options[:password_reset_token_valid_for] = options[:password_reset_token_valid_for].to_i
options[:logged_in_timeout] ||= 10.minutes
options[:logged_in_timeout] = options[:logged_in_timeout].to_i
options[:session_ids] ||= [nil]
View
11 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb
@@ -13,7 +13,7 @@ module ActsAsAuthentic
# === Instance Methods
#
# * <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.
- # * <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.
+ # * <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.
# * <tt>reset_{options[:password_field]}</tt> - resets the password using the friendly_unique_token class method
# * <tt>reset_{options[:password_field]}!</tt> - calls reset_password and then saves the record
module Credentials
@@ -29,6 +29,12 @@ def acts_as_authentic_with_credentials(options = {})
validates_length_of options[:login_field], :within => 2..100, :allow_blank => true
validates_format_of options[:login_field], :with => options[:login_field_regex], :message => options[:login_field_regex_failed_message]
end
+
+ if options[:email_field]
+ validates_length_of options[:email_field], :within => 6..100
+ validates_format_of options[:email_field], :with => options[:email_field_regex], :message => options[:email_field_regex_failed_message]
+ validates_uniqueness_of options[:email_field], :scope => options[:scope]
+ end
validates_uniqueness_of options[:login_field], :scope => options[:scope]
validate :validate_password
@@ -54,8 +60,7 @@ def #{options[:password_field]}=(pass)
def valid_#{options[:password_field]}?(attempted_password)
return false if attempted_password.blank? || #{options[:crypted_password_field]}.blank? || #{options[:password_salt_field]}.blank?
- attempted_password == #{options[:crypted_password_field]} ||
- (#{options[:crypto_provider]}.respond_to?(:decrypt) && #{options[:crypto_provider]}.decrypt(#{options[:crypted_password_field]}) == attempted_password + #{options[:password_salt_field]}) ||
+ (#{options[:crypto_provider]}.respond_to?(:decrypt) && #{options[:crypto_provider]}.decrypt(#{options[:crypted_password_field]}) == attempted_password + #{options[:password_salt_field]}) ||
(!#{options[:crypto_provider]}.respond_to?(:decrypt) && #{options[:crypto_provider]}.encrypt(attempted_password + #{options[:password_salt_field]}) == #{options[:crypted_password_field]})
end
View
73 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/password_reset.rb
@@ -0,0 +1,73 @@
+module Authlogic
+ module ORMAdapters
+ module ActiveRecordAdapter
+ module ActsAsAuthentic
+ # = Password Reset
+ #
+ # 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
+ # can change their password.
+ #
+ # === Class Methods
+ #
+ # * <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
+ # {options[:password_reset_token_valid_for]} ago. Lastly, if a blank token is passed no record will be returned.
+ #
+ # === Instance Methods
+ #
+ # * <tt>reset_#{options[:password_reset_token_field]}</tt> - resets the password reset token field to a friendly unique token.
+ # * <tt>reset_#{options[:password_reset_token_field]}!</tt> - same as above but saves the record afterwards.
+ module PasswordReset
+ def acts_as_authentic_with_password_reset(options = {})
+ acts_as_authentic_without_password_reset(options)
+
+ return if options[:password_reset_token_field].blank?
+
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ validates_uniqueness_of :#{options[:password_reset_token_field]}
+
+ before_validation :reset_#{options[:password_reset_token_field]}, :unless => :resetting_#{options[:password_reset_token_field]}?
+
+ def self.find_using_#{options[:password_reset_token_field]}(token)
+ return if token.blank?
+
+ conditions_sql = "#{options[:password_reset_token_field]} = ?"
+ conditions_subs = [token]
+
+ if column_names.include?("updated_at") && #{options[:password_reset_token_valid_for]} > 0
+ conditions_sql += " and updated_at > ?"
+ conditions_subs << #{options[:password_reset_token_valid_for]}.seconds.ago
+ end
+
+ find(:first, :conditions => [conditions_sql, *conditions_subs])
+ end
+
+ def reset_#{options[:password_reset_token_field]}
+ self.#{options[:password_reset_token_field]} = self.class.friendly_unique_token
+ end
+
+ def reset_#{options[:password_reset_token_field]}!
+ reset_#{options[:password_reset_token_field]}
+ @resetting_#{options[:password_reset_token_field]} = true
+ result = save_without_session_maintenance
+ @resetting_#{options[:password_reset_token_field]} = false
+ result
+ end
+
+ private
+ def resetting_#{options[:password_reset_token_field]}?
+ @resetting_#{options[:password_reset_token_field]} == true
+ end
+ end_eval
+ end
+ end
+ end
+ end
+ end
+end
+
+ActiveRecord::Base.class_eval do
+ class << self
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::PasswordReset
+ alias_method_chain :acts_as_authentic, :password_reset
+ end
+end
View
54 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb
@@ -18,34 +18,34 @@ module ActsAsAuthentic
module SingleAccess
def acts_as_authentic_with_single_access(options = {})
acts_as_authentic_without_single_access(options)
-
- if options[:single_access_token_field]
- class_eval <<-"end_eval", __FILE__, __LINE__
- validates_uniqueness_of :#{options[:single_access_token_field]}
-
- before_validation :set_#{options[:single_access_token_field]}_field
-
- def password_with_single_access=(value)
- reset_#{options[:single_access_token_field]} if #{options[:change_single_access_token_with_password].inspect}
- self.password_without_single_access = value
+
+ return if options[:single_access_token_field].blank?
+
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ validates_uniqueness_of :#{options[:single_access_token_field]}
+
+ before_validation :set_#{options[:single_access_token_field]}_field
+
+ def password_with_single_access=(value)
+ reset_#{options[:single_access_token_field]} if #{options[:change_single_access_token_with_password].inspect}
+ self.password_without_single_access = value
+ end
+ alias_method_chain :password=, :single_access
+
+ def reset_#{options[:single_access_token_field]}
+ self.#{options[:single_access_token_field]} = self.class.friendly_unique_token
+ end
+
+ def reset_#{options[:single_access_token_field]}!
+ reset_#{options[:single_access_token_field]}
+ save_without_session_maintenance
+ end
+
+ protected
+ def set_#{options[:single_access_token_field]}_field
+ reset_#{options[:single_access_token_field]} if #{options[:single_access_token_field]}.blank?
end
- alias_method_chain :password=, :single_access
-
- def reset_#{options[:single_access_token_field]}
- self.#{options[:single_access_token_field]} = self.class.friendly_unique_token
- end
-
- def reset_#{options[:single_access_token_field]}!
- reset_#{options[:single_access_token_field]}
- save_without_session_maintenance
- end
-
- protected
- def set_#{options[:single_access_token_field]}_field
- reset_#{options[:single_access_token_field]} if #{options[:single_access_token_field]}.blank?
- end
- end_eval
- end
+ end_eval
end
end
end
View
79 lib/authlogic/session/base.rb
@@ -8,22 +8,18 @@ class Base
class << self
# Returns true if a controller have been set and can be used properly. This MUST be set before anything can be done. Similar to how ActiveRecord won't allow you to do anything
- # without establishing a DB connection. By default this is done for you automatically, but if you are using Authlogic in a unique way outside of rails, you need to assign a controller
+ # without establishing a DB connection. In your framework environment this is done for you, but if you are using Authlogic outside of your frameword, you need to assign a controller
# object to Authlogic via Authlogic::Session::Base.controller = obj.
def activated?
!controller.blank?
end
def controller=(value) # :nodoc:
- controllers[Thread.current] = value
+ Thread.current[:authlogic_controller] = value
end
def controller # :nodoc:
- controllers[Thread.current]
- end
-
- def reset_controllers!
- @@controllers = {}
+ Thread.current[:authlogic_controller]
end
# A convenince method. The same as:
@@ -41,16 +37,26 @@ def create!(*args)
session.save!
end
- # A convenience method for session.find_record. Finds your session by session, then cookie, and finally basic http auth. Perfect for that global before_filter to find your logged in user:
+ # A convenience method for session.find_record. Finds your session by parameters, then session, then cookie, and finally by basic http auth.
+ # This is perfect for persisting your session:
+ #
+ # helper_method :current_user_session, :current_user
#
- # before_filter :load_user
+ # def current_user_session
+ # return @current_user_session if defined?(@current_user_session)
+ # @current_user_session = UserSession.find
+ # end
#
- # def load_user
- # @user_session = UserSession.find
- # @current_user = @user_session && @user_session.user
+ # def current_user
+ # return @current_user if defined?(@current_user)
+ # @current_user = current_user_session && current_user_session.user
# end
#
- # Accepts a single parameter as the id. See initialize for more information on ids. Lastly, how it finds the session can be modified via configuration.
+ # Accepts a single parameter as the id, to find session that you marked with an id:
+ #
+ # UserSession.find(:secure)
+ #
+ # See the id method for more information on ids.
def find(id = nil)
args = [id].compact
session = new(*args)
@@ -58,7 +64,9 @@ def find(id = nil)
nil
end
- def klass # :nodoc:
+ # The name of the class that this session is authenticating with. For example, the UserSession class will authenticate with the User class
+ # unless you specify otherwise in your configuration.
+ def klass
@klass ||=
if klass_name
klass_name.constantize
@@ -67,17 +75,13 @@ def klass # :nodoc:
end
end
- def klass_name # :nodoc:
+ # Same as klass, just returns a string instead of the actual constant.
+ def klass_name
@klass_name ||=
if guessed_name = name.scan(/(.*)Session/)[0]
@klass_name = guessed_name[0]
end
end
-
- private
- def controllers
- @@controllers ||= {}
- end
end
attr_accessor :new_session
@@ -96,10 +100,10 @@ def controllers
# UserSession.new({:login => "login", :password => "password", :remember_me => true}, :my_id)
# UserSession.new(User.first, true, :my_id)
#
- # Ids are rarely used, but they can be useful. For example, what if users allow other users to login into their account via proxy? Now that user can "technically" be logged into 2 accounts at once.
- # To solve this just pass a id called :proxy, or whatever you want. Authlogic will separate everything out.
+ # For more information on ids see the id method.
#
- # The reason the id is separate from the first parameter hash is becuase this should be controlled by you, not by what the user passes. A usr could inject their own id and things would not work as expected.
+ # Lastly, the reason the id is separate from the first parameter hash is becuase this should be controlled by you, not by what the user passes.
+ # A user could inject their own id and things would not work as expected.
def initialize(*args)
raise NotActivated.new(self) unless self.class.activated?
@@ -117,8 +121,8 @@ def initialize(*args)
# A flag for how the user is logging in. Possible values:
#
- # * :password - username and password
- # * :unauthorized_record - an actual ActiveRecord object
+ # * <tt>:password</tt> - username and password
+ # * <tt>:unauthorized_record</tt> - an actual ActiveRecord object
#
# By default this is :password
def authenticating_with
@@ -176,7 +180,7 @@ def errors
@errors ||= Errors.new(self)
end
- # Attempts to find the record by session, then cookie, and finally basic http auth. See the class level find method if you are wanting to use this in a before_filter to persist your session.
+ # Attempts to find the record by params, then session, then cookie, and finally basic http auth. See the class level find method if you are wanting to use this to persist your session.
def find_record
if record
self.new_session = false
@@ -202,14 +206,13 @@ def find_record
# and a "secure" user session. The secure user session would be created only when they want to modify their billing information, or other sensative information. Similar to me.com. This requires 2
# user sessions. Just use an id for the "secure" session and you should be good.
#
- # You can set the id a number of ways:
+ # You can set the id during initialization (see initialize for more information), or as an attribute:
+ #
+ # session.id = :my_id
#
- # session = Session.new(:secure)
- # session = Session.new("username", "password", :secure)
- # session = Session.new({:username => "username", :password => "password"}, :secure)
- # session.id = :secure
+ # Just be sure and set your id before you save your session.
#
- # Just be sure and set your id before you validate / create / update your session.
+ # Lastly, to retrieve your session with the id check out the find class method.
def id
@id
end
@@ -393,26 +396,26 @@ def valid_credentials?
case authenticating_with
when :password
- errors.add(login_field, "can not be blank") if send(login_field).blank?
- errors.add(password_field, "can not be blank") if send("protected_#{password_field}").blank?
+ errors.add(login_field, login_blank_message) if send(login_field).blank?
+ errors.add(password_field, password_blank_message) if send("protected_#{password_field}").blank?
return false if errors.count > 0
unchecked_record = search_for_record(find_by_login_method, send(login_field))
if unchecked_record.blank?
- errors.add(login_field, "was not found")
+ errors.add(login_field, login_not_found_message)
return false
end
unless unchecked_record.send(verify_password_method, send("protected_#{password_field}"))
- errors.add(password_field, "is invalid")
+ errors.add(password_field, password_invalid_message)
return false
end
when :unauthorized_record
unchecked_record = unauthorized_record
if unchecked_record.blank?
- errors.add_to_base("The record could not be found and did not match the requirements.")
+ errors.add_to_base("You can not login with a blank record.")
return false
end
@@ -429,7 +432,7 @@ def valid_credentials?
def valid_record?
[:active, :approved, :confirmed].each do |required_status|
if record.respond_to?("#{required_status}?") && !record.send("#{required_status}?")
- errors.add_to_base("Your account has not been marked as #{required_status}")
+ errors.add_to_base(send("not_#{required_status}_message"))
return false
end
end
View
157 lib/authlogic/session/config.rb
@@ -92,7 +92,7 @@ def find_by_login_method(value = nil)
# Calling UserSession.find tries to find the user session by session, then cookie, then params, and finally by basic http auth.
# This option allows you to change the order or remove any of these.
#
- # * <tt>Default:</tt> [:session, :cookie, :params, :http_auth]
+ # * <tt>Default:</tt> [:params, :session, :cookie, :http_auth]
# * <tt>Accepts:</tt> Array, and can only use any of the 3 options above
def find_with(*values)
if values.blank?
@@ -119,6 +119,32 @@ def last_request_at_threshold(value = nil)
end
alias_method :last_request_at_threshold=, :last_request_at_threshold
+ # The error message used when the login is left blank.
+ #
+ # * <tt>Default:</tt> "can not be blank"
+ # * <tt>Accepts:</tt> String
+ def login_blank_message(value = nil)
+ if value.nil?
+ read_inheritable_attribute(:login_blank_message) || login_blank_message("can not be blank")
+ else
+ write_inheritable_attribute(:login_blank_message, value)
+ end
+ end
+ alias_method :login_blank_message=, :login_blank_message
+
+ # The error message used when the login could not be found in the database.
+ #
+ # * <tt>Default:</tt> "does not exist"
+ # * <tt>Accepts:</tt> String
+ def login_not_found_message(value = nil)
+ if value.nil?
+ read_inheritable_attribute(:login_not_found_message) || login_not_found_message("does not exist")
+ else
+ write_inheritable_attribute(:login_not_found_message, value)
+ end
+ end
+ alias_method :login_not_found_message=, :login_not_found_message
+
# The name of the method you want Authlogic to create for storing the login / username. Keep in mind this is just for your
# Authlogic::Session, if you want it can be something completely different than the field in your model. So if you wanted people to
# login with a field called "login" and then find users by email this is compeltely doable. See the find_by_login_method configuration
@@ -135,6 +161,45 @@ def login_field(value = nil)
end
alias_method :login_field=, :login_field
+ # The error message used when the record returns false to active?
+ #
+ # * <tt>Default:</tt> "Your account is not active"
+ # * <tt>Accepts:</tt> String
+ def not_active_message(value = nil)
+ if value.nil?
+ read_inheritable_attribute(:not_active_message) || not_active_message("Your account is not active")
+ else
+ write_inheritable_attribute(:not_active_message, value)
+ end
+ end
+ alias_method :not_active_message=, :not_active_message
+
+ # The error message used when the record returns false to approved?
+ #
+ # * <tt>Default:</tt> "Your account is not approved"
+ # * <tt>Accepts:</tt> String
+ def not_approved_message(value = nil)
+ if value.nil?
+ read_inheritable_attribute(:not_approved_message) || not_active_message("Your account is not approved")
+ else
+ write_inheritable_attribute(:not_approved_message, value)
+ end
+ end
+ alias_method :not_approved_message=, :not_approved_message
+
+ # The error message used when the record returns false to confirmed?
+ #
+ # * <tt>Default:</tt> "Your account is not confirmed"
+ # * <tt>Accepts:</tt> String
+ def not_confirmed_message(value = nil)
+ if value.nil?
+ read_inheritable_attribute(:not_confirmed_message) || not_active_message("Your account is not confirmed")
+ else
+ write_inheritable_attribute(:not_confirmed_message, value)
+ end
+ end
+ alias_method :not_confirmed_message=, :not_confirmed_message
+
# Works exactly like cookie_key, but for params. So a user can login via params just like a cookie or a session. Your URL would look like:
#
# http://www.domain.com?user_credentials=my_single_access_key
@@ -153,6 +218,18 @@ def params_key(value = nil)
end
alias_method :params_key=, :params_key
+ # The error message used when the password is left blank.
+ #
+ # * <tt>Default:</tt> "can not be blank"
+ # * <tt>Accepts:</tt> String
+ def password_blank_message(value = nil)
+ if value.nil?
+ read_inheritable_attribute(:password_blank_message) || password_blank_message("can not be blank")
+ else
+ write_inheritable_attribute(:password_blank_message, value)
+ end
+ end
+ alias_method :password_blank_message=, :password_blank_message
# Works exactly like login_field, but for the password instead.
#
@@ -167,6 +244,19 @@ def password_field(value = nil)
end
alias_method :password_field=, :password_field
+ # The error message used when the password is invalid.
+ #
+ # * <tt>Default:</tt> "is invalid"
+ # * <tt>Accepts:</tt> String
+ def password_invalid_message(value = nil)
+ if value.nil?
+ read_inheritable_attribute(:password_invalid_message) || login_not_found_message("is invalid")
+ else
+ write_inheritable_attribute(:password_invalid_message, value)
+ end
+ end
+ alias_method :password_invalid_message=, :password_invalid_message
+
# If sessions should be remembered by default or not.
#
# * <tt>Default:</tt> false
@@ -193,21 +283,6 @@ def remember_me_for(value = :_read)
end
alias_method :remember_me_for=, :remember_me_for
- # The name of the field that the remember token is stored. This is for cookies. Let's say you set up your app and want all users to be remembered for 6 months. Then you realize that might be a little too
- # long. Well they already have a cookie set to expire in 6 months. Without a token you would have to reset their password, which obviously isn't feasible. So instead of messing with their password
- # just reset their remember token. Next time they access the site and try to login via a cookie it will be rejected and they will have to relogin.
- #
- # * <tt>Default:</tt> Uses the configuration option in your model: User.acts_as_authentic_config[:remember_token_field]
- # * <tt>Accepts:</tt> Symbol or String
- def remember_token_field(value = nil)
- if value.nil?
- read_inheritable_attribute(:remember_token_field) || remember_token_field(klass.acts_as_authentic_config[:remember_token_field])
- else
- write_inheritable_attribute(:remember_token_field, value)
- end
- end
- alias_method :remember_token_field=, :remember_token_field
-
# Works exactly like cookie_key, but for sessions. See cookie_key for more info.
#
# * <tt>Default:</tt> cookie_key
@@ -235,20 +310,6 @@ def single_access_allowed_request_types(*values)
end
alias_method :single_access_allowed_request_types=, :single_access_allowed_request_types
- # This is a separate token for logging with single access. It works just the the remember_token but it does NOT persist. Meaning if a record is found with the single_access_token it will not set
- # the session or the cookie and "remember" the user. Checkout the "Single Access / Private Feeds Access" section in the README.
- #
- # * <tt>Default:</tt> Uses the configuration option in your model: User.acts_as_authentic_config[:single_access_token]
- # * <tt>Accepts:</tt> Symbol or String
- def single_access_token_field(value = nil)
- if value.nil?
- read_inheritable_attribute(:single_access_token_field) || single_access_token_field(klass.acts_as_authentic_config[:single_access_token_field])
- else
- write_inheritable_attribute(:single_access_token_field, value)
- end
- end
- alias_method :single_access_token_field=, :single_access_token_field
-
# 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.
#
# * <tt>Default:</tt> "valid_#{password_field}?"
@@ -283,11 +344,31 @@ def find_with
def last_request_at_threshold
self.class.last_request_at_threshold
end
+
+ def login_blank_message
+ self.class.login_blank_message
+ end
+
+ def login_not_found_message
+ self.class.login_not_found_message
+ end
def login_field
self.class.login_field
end
+ def not_active_message
+ self.class.not_active_message
+ end
+
+ def not_approved_message
+ self.class.not_approved_message
+ end
+
+ def not_confirmed_message
+ self.class.not_confirmed_message
+ end
+
def params_allowed_request_types
build_key(self.class.params_allowed_request_types)
end
@@ -295,18 +376,30 @@ def params_allowed_request_types
def params_key
build_key(self.class.params_key)
end
+
+ def password_blank_message
+ self.class.password_blank_message
+ end
def password_field
self.class.password_field
end
+ def password_invalid_message
+ self.class.password_invalid_message
+ end
+
+ def password_reset_token_field
+ klass.acts_as_authentic_config[:password_reset_token_field]
+ end
+
def remember_me_for
return unless remember_me?
self.class.remember_me_for
end
def remember_token_field
- self.class.remember_token_field
+ klass.acts_as_authentic_config[:remember_token_field]
end
def session_key
@@ -314,7 +407,7 @@ def session_key
end
def single_access_token_field
- self.class.single_access_token_field
+ klass.acts_as_authentic_config[:single_access_token_field]
end
def single_access_allowed_request_types
View
17 lib/authlogic/session/password_reset.rb
@@ -0,0 +1,17 @@
+module Authlogic
+ module Session
+ # = Password Reset
+ #
+ # Provides utilities that assist in maintaining the password reset token. This module just resets the token after a session has been saved, just to keep changing it and add extra security.
+ module PasswordReset
+ def self.included(klass)
+ klass.after_save :reset_password_reset_token!
+ end
+
+ private
+ def reset_password_reset_token!
+ record.send("reset_#{password_reset_token_field}!") if record.respond_to?("reset_#{password_reset_token_field}!")
+ end
+ end
+ end
+end
View
8 lib/authlogic/session/scopes.rb
@@ -57,7 +57,7 @@ def self.included(klass)
module ClassMethods
# The current scope set, should be used in the block passed to with_scope.
def scope
- scopes[Thread.current]
+ Thread.current[:authlogic_scope]
end
# See the documentation for this class for more information on how to use this method.
@@ -71,11 +71,7 @@ def with_scope(options = {}, &block)
private
def scope=(value)
- scopes[Thread.current] = value
- end
-
- def scopes
- @scopes ||= {}
+ Thread.current[:authlogic_scope] = value
end
end
View
4 lib/authlogic/version.rb
@@ -43,8 +43,8 @@ def to_a
end
MAJOR = 1
- MINOR = 1
- TINY = 2
+ MINOR = 2
+ TINY = 0
# The current version as a Version instance
CURRENT = new(MAJOR, MINOR, TINY)
View
3  test/fixtures/users.yml
@@ -6,6 +6,8 @@ ben:
crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("benrocks" + salt) %>
remember_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
single_access_token: <%= User.friendly_unique_token %>
+ password_reset_token: <%= User.friendly_unique_token %>
+ email: bjohnson@binarylogic.com
first_name: Ben
last_name: Johnson
@@ -17,5 +19,6 @@ zack:
crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("zackrocks" + salt) %>
remember_token: fd3c2d5ce09ab98e7547d21f1b3dcf9158a9a19b5d3022c0402f32ae197019fce3fdbc6614d7ee57d719bae53bb089e30edc9e5d6153e5bc3afca0ac1d320342
single_access_token: <%= User.friendly_unique_token %>
+ email: zham@ziggityzack.com
first_name: Zack
last_name: Ham
View
7 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb
@@ -21,12 +21,17 @@ def test_acts_as_authentic_config
:password_field => :password,
:logged_in_timeout => 600,
:password_salt_field => :password_salt,
+ :password_reset_token_valid_for => 600,
+ :password_reset_token_field => :password_reset_token,
:login_field_type => :login,
:crypto_provider => Authlogic::CryptoProviders::Sha512,
:password_blank_message => "can not be blank",
:crypted_password_field => :crypted_password,
:session_class => "UserSession",
- :login_field => :login
+ :login_field => :login,
+ :email_field => :email,
+ :email_field_regex => /\A[\w\.%\+\-]+@(?:[A-Z0-9\-]+\.)+(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|jobs|museum)\z/i,
+ :email_field_regex_failed_message=>"should look like an email address."
}
assert_equal default_config, User.acts_as_authentic_config
end
View
18 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb
@@ -9,26 +9,31 @@ def test_user_validations
assert !user.valid?
assert user.errors.on(:login)
assert user.errors.on(:password)
+ assert user.errors.on(:email)
user.login = "a"
assert !user.valid?
assert user.errors.on(:login)
assert user.errors.on(:password)
+ assert user.errors.on(:email)
user.login = "%ben*"
assert !user.valid?
assert user.errors.on(:login)
assert user.errors.on(:password)
+ assert user.errors.on(:email)
user.login = "bjohnson"
assert !user.valid?
assert user.errors.on(:login)
assert user.errors.on(:password)
+ assert user.errors.on(:email)
user.login = "my login"
assert !user.valid?
assert !user.errors.on(:login)
assert user.errors.on(:password)
+ assert user.errors.on(:email)
user.password = "my pass"
assert !user.valid?
@@ -39,8 +44,17 @@ def test_user_validations
assert !user.valid?
assert !user.errors.on(:password)
assert user.errors.on(:confirm_password)
+ assert user.errors.on(:email)
user.confirm_password = "my pass"
+ assert !user.valid?
+ assert user.errors.on(:email)
+
+ user.email = "some email"
+ assert !user.valid?
+ assert user.errors.on(:email)
+
+ user.email = "a@a.com"
assert user.valid?
end
@@ -95,11 +109,11 @@ def test_password
def test_valid_password
ben = users(:ben)
assert ben.valid_password?("benrocks")
- assert ben.valid_password?(ben.crypted_password)
+ assert !ben.valid_password?(ben.crypted_password)
drew = employees(:drew)
assert drew.valid_password?("drewrocks")
- assert drew.valid_password?(drew.crypted_password)
+ assert !drew.valid_password?(drew.crypted_password)
end
def test_reset_password
View
40 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/password_reset_test.rb
@@ -0,0 +1,40 @@
+require File.dirname(__FILE__) + '/../../../test_helper.rb'
+
+module ORMAdaptersTests
+ module ActiveRecordAdapterTests
+ module ActsAsAuthenticTests
+ class PasswordResetTest < ActiveSupport::TestCase
+ def test_before_validation
+ ben = users(:ben)
+ old_password_reset_token = ben.password_reset_token
+ assert ben.valid?
+ assert_not_equal old_password_reset_token, ben.password_reset_token
+ ben.reload
+ assert_equal old_password_reset_token, ben.password_reset_token
+ assert ben.save
+ assert_not_equal old_password_reset_token, ben.password_reset_token
+ end
+
+ def test_find_using_password_reset_token
+ ben = users(:ben)
+ assert_nil User.find_using_password_reset_token("")
+ assert_equal ben, User.find_using_password_reset_token(ben.password_reset_token)
+ assert ben.class.connection.execute("update users set updated_at = '#{10.minutes.ago.to_s(:db)}' where id = '#{ben.id}';")
+ assert_nil User.find_using_password_reset_token(ben.password_reset_token)
+ end
+
+ def test_reset_password_reset_token
+ ben = users(:ben)
+ old_password_reset_token = ben.password_reset_token
+ ben.reset_password_reset_token
+ assert_not_equal old_password_reset_token, ben.password_reset_token
+ ben.reload
+ assert_equal old_password_reset_token, ben.password_reset_token
+ ben.reset_password_reset_token!
+ ben.reload
+ assert_not_equal old_password_reset_token, ben.password_reset_token
+ end
+ end
+ end
+ end
+end
View
2  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb
@@ -5,7 +5,7 @@ module ActiveRecordAdapterTests
module ActsAsAuthenticTests
class SessionMaintenanceTest < ActiveSupport::TestCase
def test_login_after_create
- assert User.create(:login => "awesome", :password => "saweet", :confirm_password => "saweet")
+ assert User.create(:login => "awesome", :password => "saweet", :confirm_password => "saweet", :email => "awesome@awesome.com")
assert UserSession.find
end
View
17 test/session_tests/base_test.rb
@@ -4,28 +4,29 @@ module SessionTests
class BaseTest < ActiveSupport::TestCase
def test_activated
assert UserSession.activated?
- Authlogic::Session::Base.reset_controllers!
+ Authlogic::Session::Base.controller = nil
assert !UserSession.activated?
end
- def test_controllers
- Authlogic::Session::Base.reset_controllers!
- assert_equal 0, Authlogic::Session::Base.send(:controllers).size
+ def test_controller
+ Authlogic::Session::Base.controller = nil
+ assert_nil Authlogic::Session::Base.controller
thread1 = Thread.new do
controller = MockController.new
Authlogic::Session::Base.controller = controller
assert_equal controller, Authlogic::Session::Base.controller
end
thread1.join
- assert_equal 1, Authlogic::Session::Base.send(:controllers).size
+
assert_nil Authlogic::Session::Base.controller
+
thread2 = Thread.new do
controller = MockController.new
Authlogic::Session::Base.controller = controller
assert_equal controller, Authlogic::Session::Base.controller
end
thread2.join
- assert_equal 2, Authlogic::Session::Base.send(:controllers).size
+
assert_nil Authlogic::Session::Base.controller
end
@@ -82,7 +83,7 @@ def test_record_method
end
def test_init
- UserSession.reset_controllers!
+ UserSession.controller = nil
assert_raise(Authlogic::Session::NotActivated) { UserSession.new }
UserSession.controller = @controller
@@ -272,7 +273,7 @@ def test_valid_http_auth
assert session.valid_http_auth?
assert_equal ben, session.record
assert_equal ben.login, session.login
- assert_equal ben.crypted_password, session.send(:protected_password)
+ assert_equal "benrocks", session.send(:protected_password)
end
end
end
View
96 test/session_tests/config_test.rb
@@ -65,6 +65,30 @@ def test_last_request_at_threshold
assert_equal 0, session.last_request_at_threshold
end
+ def test_login_blank_message
+ UserSession.login_blank_message = "message"
+ assert_equal "message", UserSession.login_blank_message
+ session = UserSession.new
+ assert_equal "message", session.login_blank_message
+
+ UserSession.login_blank_message "can not be blank"
+ assert_equal "can not be blank", UserSession.login_blank_message
+ session = UserSession.new
+ assert_equal "can not be blank", session.login_blank_message
+ end
+
+ def test_login_not_found_message
+ UserSession.login_not_found_message = "message"
+ assert_equal "message", UserSession.login_not_found_message
+ session = UserSession.new
+ assert_equal "message", session.login_not_found_message
+
+ UserSession.login_not_found_message "does not exist"
+ assert_equal "does not exist", UserSession.login_not_found_message
+ session = UserSession.new
+ assert_equal "does not exist", session.login_not_found_message
+ end
+
def test_login_field
UserSession.login_field = :saweet
assert_equal :saweet, UserSession.login_field
@@ -79,6 +103,42 @@ def test_login_field
assert session.respond_to?(:login)
end
+ def test_not_active_message
+ UserSession.not_active_message = "message"
+ assert_equal "message", UserSession.not_active_message
+ session = UserSession.new
+ assert_equal "message", session.not_active_message
+
+ UserSession.not_active_message "Your account is not active"
+ assert_equal "Your account is not active", UserSession.not_active_message
+ session = UserSession.new
+ assert_equal "Your account is not active", session.not_active_message
+ end
+
+ def test_not_approved_message
+ UserSession.not_approved_message = "message"
+ assert_equal "message", UserSession.not_approved_message
+ session = UserSession.new
+ assert_equal "message", session.not_approved_message
+
+ UserSession.not_approved_message "Your account is not approved"
+ assert_equal "Your account is not approved", UserSession.not_approved_message
+ session = UserSession.new
+ assert_equal "Your account is not approved", session.not_approved_message
+ end
+
+ def test_not_confirmed_message
+ UserSession.not_confirmed_message = "message"
+ assert_equal "message", UserSession.not_confirmed_message
+ session = UserSession.new
+ assert_equal "message", session.not_confirmed_message
+
+ UserSession.not_confirmed_message "Your account is not confirmed"
+ assert_equal "Your account is not confirmed", UserSession.not_confirmed_message
+ session = UserSession.new
+ assert_equal "Your account is not confirmed", session.not_confirmed_message
+ end
+
def test_params_key
UserSession.params_key = "my_params_key"
assert_equal "my_params_key", UserSession.params_key
@@ -90,6 +150,18 @@ def test_params_key
session = UserSession.new
assert_equal "user_credentials", session.params_key
end
+
+ def test_password_blank_message
+ UserSession.password_blank_message = "message"
+ assert_equal "message", UserSession.password_blank_message
+ session = UserSession.new
+ assert_equal "message", session.password_blank_message
+
+ UserSession.password_blank_message "can not be blank"
+ assert_equal "can not be blank", UserSession.password_blank_message
+ session = UserSession.new
+ assert_equal "can not be blank", session.password_blank_message
+ end
def test_password_field
UserSession.password_field = :saweet
@@ -104,6 +176,18 @@ def test_password_field
assert_equal :password, session.password_field
assert session.respond_to?(:password)
end
+
+ def test_password_invalid_message
+ UserSession.password_invalid_message = "message"
+ assert_equal "message", UserSession.password_invalid_message
+ session = UserSession.new
+ assert_equal "message", session.password_invalid_message
+
+ UserSession.password_invalid_message "is invalid"
+ assert_equal "is invalid", UserSession.password_invalid_message
+ session = UserSession.new
+ assert_equal "is invalid", session.password_invalid_message
+ end
def test_remember_me
UserSession.remember_me = true
@@ -131,18 +215,6 @@ def test_remember_me_for
assert_equal 3.months, session.remember_me_for
end
- def test_remember_token_field
- UserSession.remember_token_field = :saweet
- assert_equal :saweet, UserSession.remember_token_field
- session = UserSession.new
- assert_equal :saweet, session.remember_token_field
-
- UserSession.remember_token_field :remember_token
- assert_equal :remember_token, UserSession.remember_token_field
- session = UserSession.new
- assert_equal :remember_token, session.remember_token_field
- end
-
def test_session_key
UserSession.session_key = "my_session_key"
assert_equal "my_session_key", UserSession.session_key
View
15 test/session_tests/password_reset_test.rb
@@ -0,0 +1,15 @@
+require File.dirname(__FILE__) + '/../test_helper.rb'
+
+module SessionTests
+ class PasswordResetTest < ActiveSupport::TestCase
+ def test_after_save
+ ben = users(:ben)
+ old_password_reset_token = ben.password_reset_token
+ session = UserSession.create(ben)
+ assert_not_equal old_password_reset_token, ben.password_reset_token
+
+ drew = employees(:drew)
+ assert UserSession.create(drew)
+ end
+ end
+end
View
9 test/session_tests/scopes_test.rb
@@ -3,24 +3,25 @@
module SessionTests
class ScopesTest < ActiveSupport::TestCase
def test_scope_method
- assert_equal 0, Authlogic::Session::Base.send(:scopes).size
+ assert_nil Authlogic::Session::Base.scope
+
thread1 = Thread.new do
scope = {:id => :scope1}
Authlogic::Session::Base.send(:scope=, scope)
assert_equal scope, Authlogic::Session::Base.scope
end
thread1.join
- assert_equal 1, Authlogic::Session::Base.send(:scopes).size
+
assert_nil Authlogic::Session::Base.scope
+
thread2 = Thread.new do
scope = {:id => :scope2}
Authlogic::Session::Base.send(:scope=, scope)
assert_equal scope, Authlogic::Session::Base.scope
end
thread2.join
- assert_equal 2, Authlogic::Session::Base.send(:scopes).size
+
assert_nil Authlogic::Session::Base.scope
- Authlogic::Session::Base.send(:scopes).clear
end
def test_with_scope_method
View
18 test/test_helper.rb
@@ -40,6 +40,8 @@
t.string :password_salt
t.string :remember_token
t.string :single_access_token
+ t.string :password_reset_token
+ t.string :email
t.string :first_name
t.string :last_name
t.integer :login_count
@@ -104,22 +106,26 @@ class Test::Unit::TestCase
self.pre_loaded_fixtures = true
fixtures :all
setup :activate_authlogic
- teardown :deactivate_authlogic
private
def activate_authlogic
@controller = MockController.new
Authlogic::Session::Base.controller = @controller
end
-
- def deactivate_authlogic
- Authlogic::Session::Base.reset_controllers!
- end
def http_basic_auth_for(user = nil, &block)
unless user.blank?
@controller.http_user = user.login
- @controller.http_password = user.crypted_password
+
+ password = nil
+ case user
+ when users(:ben)
+ password = "benrocks"
+ when users(:zack)
+ password = "zackrocks"
+ end
+
+ @controller.http_password = password
end
yield
@controller.http_user = @controller.http_password = nil
Please sign in to comment.
Something went wrong with that request. Please try again.