Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Released v1.1.1

  • Loading branch information...
commit ebdebfa942a0b5faf1d26dd1bb5d02fc05accf80 1 parent 4bdf3fd
@binarylogic authored
Showing with 1,006 additions and 774 deletions.
  1. +11 −2 CHANGELOG.rdoc
  2. +9 −3 Manifest
  3. +32 −7 README.rdoc
  4. +1 −1  Rakefile
  5. +2 −4 lib/authlogic.rb
  6. +4 −0 lib/authlogic/controller_adapters/abstract_adapter.rb
  7. +4 −0 lib/authlogic/controller_adapters/rails_adapter.rb
  8. +2 −76 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb
  9. +132 −0 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb
  10. +77 −116 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb
  11. +35 −24 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb
  12. +51 −44 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb
  13. +64 −54 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb
  14. +61 −0 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb
  15. +20 −9 lib/authlogic/session/base.rb
  16. +54 −63 lib/authlogic/session/config.rb
  17. +2 −2 lib/authlogic/session/cookies.rb
  18. +0 −106 lib/authlogic/session/openid.rb
  19. +9 −6 lib/authlogic/session/params.rb
  20. +3 −3 lib/authlogic/session/session.rb
  21. +0 −17 lib/authlogic/testing/shoulda_macros.rb
  22. +1 −1  lib/authlogic/version.rb
  23. +13 −0 shoulda_macros/authlogic.rb
  24. +2 −2 test/fixtures/employees.yml
  25. +2 −0  test/fixtures/users.yml
  26. +5 −0 test/libs/mock_controller.rb
  27. +0 −217 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_test.rb
  28. +36 −0 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb
  29. +129 −0 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb
  30. +24 −0 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb
  31. +45 −0 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb
  32. +62 −0 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb
  33. +41 −0 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/single_access_test.rb
  34. +15 −0 test/session_tests/base_test.rb
  35. +31 −14 test/session_tests/config_test.rb
  36. +17 −1 test/session_tests/params_test.rb
  37. +10 −2 test/test_helper.rb
View
13 CHANGELOG.rdoc
@@ -1,4 +1,13 @@
-== 1.1.0 released 2008-11-05
+== 1.1.1 released 2008-11-13
+
+* Removed ActiveRecord dependency.
+* Removed loading shoulda macros by default, moved to shoulda_macros dir.
+* Modified how params access works. Added in single_access_token_field which params now uses. See the single access section in the README. Various configuration options added as well.
+* Cleaned up acts_as_authentic configuration, added new config module to do this.
+* Cleaned up acts_as_authentic tests
+* Moved acts_as_authentic sub modules into the proper name spaces
+
+== 1.1.0 released 2008-11-13
* Moved Rack standards into abstract_adapter for the controllers.
* Added authenticating_with_credentials?, authenticating_with_unauthorized_record?
@@ -7,7 +16,7 @@
* Moved ActiveRecord additions to ORM Adapters name space to make way for Data Mapper.
* Reorganized and modified acts_as_authentic to be free standing and not get info from the related session.
* The session now gets its configuration from the model, since determining which fields are present is ORM specific.
-* Extracted session and cookie logic into their own modules.
+* Extracted session and cookie logic into their own modules for Session.
* Moved crypto providers into their own module and added a Sha1 provider to help with the restful_authentication transition.
* Allow the unique_token method to use the alternate crypto_provider if it is a hash algorithm, otherwise default to Sha512.
* Added last_request_at_threshold configuration option.
View
12 Manifest
@@ -5,10 +5,12 @@ 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
@@ -18,17 +20,16 @@ lib/authlogic/session/callbacks.rb
lib/authlogic/session/config.rb
lib/authlogic/session/cookies.rb
lib/authlogic/session/errors.rb
-lib/authlogic/session/openid.rb
lib/authlogic/session/params.rb
lib/authlogic/session/scopes.rb
lib/authlogic/session/session.rb
-lib/authlogic/testing/shoulda_macros.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
@@ -38,7 +39,12 @@ 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_test.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
View
39 README.rdoc
@@ -31,7 +31,7 @@ What if your user sessions controller could look just like your other controller
end
def destroy
- @user_session.destroy
+ current_user_session.destroy
end
end
@@ -51,12 +51,17 @@ Look familiar? If you didn't know any better, you would think UserSession was an
Or how about persisting the session...
class ApplicationController
- before_filter :load_user
+ helper_method :current_user_session, :current_user
protected
- def load_user
- @user_session = UserSession.find
- @current_user = @user_session && @user_session.user
+ def current_user_session
+ return @current_user_session if defined?(@current_user_session)
+ @current_user_session = UserSession.find
+ end
+
+ end current_user
+ return @current_user if defined?(@current_user)
+ @current_user = current_user_session && current_user_session.user
end
end
@@ -78,7 +83,7 @@ Install the gem / plugin (recommended)
Now add the gem dependency in your config:
# config/environment.rb
- config.gem :authlogic
+ config.gem "authlogic"
Or you install this as a plugin (for older versions of rails)
@@ -111,7 +116,7 @@ The user model needs to have the following columns. The names of these columns c
Make sure you have a model that you will be authenticating with. For this example let's say you have a User model:
class User < ActiveRecord::Base
- acts_as_authentic # for options see documentation: Authlogic::ActsAsAuthentic::ClassMethods
+ 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.
@@ -179,6 +184,26 @@ This will keep everything separate. The :secure session will store its info in a
For more information on ids checkout Authlogic::Session::Base#initialize
+== 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 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:
+
+ t.string :single_access_token, :null => false # or call it feeds_token or feed_token
+
+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:
+
+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
+
+You also have the following options when calling acts_as_authentic (Authlogic::ORMAdapters::ActiveRecordAdapter::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.
+
+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.
+
== Scoping
Scoping with authentication is a little tricky because it can come in many different flavors:
View
2  Rakefile
@@ -10,6 +10,6 @@ Echoe.new 'authlogic' do |p|
p.project = 'authlogic'
p.summary = "A clean, simple, and unobtrusive ruby authentication solution."
p.url = "http://github.com/binarylogic/authlogic"
- p.dependencies = %w(activesupport activerecord)
+ p.dependencies = %w(activesupport)
p.include_rakefile = true
end
View
6 lib/authlogic.rb
@@ -15,6 +15,8 @@
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/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"
+ require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config" # call this last so the configuration options are passed down the chain
require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/authenticates_many"
end
@@ -24,21 +26,17 @@
require File.dirname(__FILE__) + "/authlogic/session/config"
require File.dirname(__FILE__) + "/authlogic/session/cookies"
require File.dirname(__FILE__) + "/authlogic/session/errors"
-#require File.dirname(__FILE__) + "/authlogic/session/openid"
require File.dirname(__FILE__) + "/authlogic/session/params"
require File.dirname(__FILE__) + "/authlogic/session/session"
require File.dirname(__FILE__) + "/authlogic/session/scopes"
require File.dirname(__FILE__) + "/authlogic/session/base"
-require File.dirname(__FILE__) + "/authlogic/testing/shoulda_macros"
-
module Authlogic
module Session
class Base
include ActiveRecordTrickery
include Callbacks
include Cookies
- #include OpenID
include Params
include Session
include Scopes
View
4 lib/authlogic/controller_adapters/abstract_adapter.rb
@@ -30,6 +30,10 @@ def request
controller.request
end
+ def request_content_type
+ request.content_type
+ end
+
def session
controller.session
end
View
4 lib/authlogic/controller_adapters/rails_adapter.rb
@@ -12,6 +12,10 @@ def cookies
controller.send(:cookies)
end
+ def request_content_type
+ request.format.to_s
+ end
+
# = Rails Implementation
# Lets Authlogic know about the controller object, AKA "activates" authlogic.
module RailsImplementation
View
78 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb
@@ -2,84 +2,10 @@ module Authlogic
module ORMAdapters # :nodoc:
module ActiveRecordAdapter # :nodoc:
# = Acts As Authentic
- # Provides the acts_as_authentic method to include in your models to help with authentication. See method below.
+ # Provides the acts_as_authentic method to include in your models to help with authentication. See sub modules for more information.
module ActsAsAuthentic
- # Call this method in your model to add in basic authentication madness that your authlogic session expects.
- #
- # === Methods
- # For example purposes lets assume you have a User model.
- #
- # Class method name Description
- # User.crypto_provider The class that you set in your :crypto_provider option
- # User.forget_all! Finds all records, loops through them, and calls forget! on each record. This is paginated to save on memory.
- # User.unique_token returns unique token generated by your :crypto_provider
- #
- # Named Scopes
- # User.logged_in Find all users who are logged in, based on your :logged_in_timeout option.
- # User.logged_out Same as above, but logged out.
- #
- # Isntace method name
- # user.password= Method name based on the :password_field option. This is used to set the password. Pass the *raw* password to this.
- # user.confirm_password= Confirms the password, needed to change the password.
- # user.valid_password?(pass) Determines if the password passed is valid. The password could be encrypted or raw.
- # user.reset_password Resets the password to a random password using only letters and numbers.
- # user.reset_password! The same as reset_password but saves the record.
- # user.logged_in? Based on the :logged_in_timeout option. Tells you if the user is logged in or not.
- # user.forget! Changes their remember token, making their cookie and session invalid. A way to log the user out withouth changing their password.
- #
- # === Options
- #
- # * <tt>session_class:</tt> default: "#{name}Session",
- # This is the related session class. A lot of the configuration will be based off of the configuration values of this class.
- #
- # * <tt>crypto_provider:</tt> default: Authlogic::CryptoProviders::Sha512,
- # This is the class that provides your encryption. By default Authlogic provides its own crypto provider that uses Sha512 encrypton.
- #
- # * <tt>login_field:</tt> default: options[:session_class].login_field,
- # The name of the field used for logging in, this is guess based on what columns are in your db. Only specify if you aren't using:
- # login, username, or email
- #
- # * <tt>login_field_type:</tt> default: options[:login_field] == :email ? :email : :login,
- # Tells authlogic how to validation the field, what regex to use, etc. If the field name is email it will automatically use email,
- # otherwise it uses login.
- #
- # * <tt>login_field_regex:</tt> default: if email then typical email regex, otherwise typical login regex.
- # This is used in validates_format_of for the login_field.
- #
- # * <tt>login_field_regex_message:</tt> the message to use when the validates_format_of for the login field fails.
- #
- # * <tt>password_field:</tt> default: options[:session_class].password_field,
- # This is the name of the field to set the password, *NOT* the field the encrypted password is stored.
- #
- # * <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>password_salt_field:</tt> default: depends on which columns are present,
- # This is the name of the field in your database that stores your password salt. If the name of the field is different from any of the
- # following then you need to specify it with this option: password_salt, pw_salt, salt
- #
- # * <tt>remember_token_field:</tt> default: options[:session_class].remember_token_field,
- # 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
- # session. This way you have complete control of when session expire and you don't have to change passwords to expire sessions. This also
- # ensures that stale sessions can not be persisted. By stale, I mean sessions that are logged in using an outdated password. If the name
- # of the field is anything other than the following you need to specify it with this option: remember_token, remember_key, cookie_token,
- # cookie_key
- #
- # * <tt>scope:</tt> default: nil,
- # This scopes validations. If all of your users belong to an account you might want to scope everything to the account. Just pass :account_id
- #
- # * <tt>logged_in_timeout:</tt> default: 10.minutes,
- # This is really just a nifty feature to tell if a user is logged in or not. It's based on activity. So if the user in inactive longer than
- # the value you pass here they are assumed "logged out".
- #
- # * <tt>session_ids:</tt> default: [nil],
- # The sessions that we want to automatically reset when a user is created or updated so you don't have to worry about this. Set to [] to disable.
- # Should be an array of ids. See the Authlogic::Session documentation for information on ids. The order is important.
- # The first id should be your main session, the session they need to log into first. This is generally nil. When you don't specify an id
- # in your session you are really just inexplicitly saying you want to use the id of nil.
+ # All logic for this method is split up into sub modules. This a stub to create a method chain off of and provide documentation. See sub modules for more details.
def acts_as_authentic(options = {})
- # All logic for this method is split up into sub modules. This a stub to create a method chain off of and provide documentation.
end
end
end
View
132 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/config.rb
@@ -0,0 +1,132 @@
+module Authlogic
+ module ORMAdapters
+ module ActiveRecordAdapter
+ module ActsAsAuthentic
+ # = Config
+ #
+ # Allows you to set various configuration when calling acts_as_authentic. Pass your configuration like the following:
+ #
+ # class User < ActiveRecord::Base
+ # acts_as_authentic :my_option => "my value"
+ # end
+ #
+ # === Class Methods
+ #
+ # * <tt>acts_as_authentic_config</tt> - returns a hash of the acts_as_authentic configuration, including the defaults
+ #
+ # === Options
+ #
+ # * <tt>session_class</tt> - default: "#{name}Session",
+ # This is the related session class. A lot of the configuration will be based off of the configuration values of this class.
+ #
+ # * <tt>crypto_provider</tt> - default: Authlogic::CryptoProviders::Sha512,
+ # This is the class that provides your encryption. By default Authlogic provides its own crypto provider that uses Sha512 encrypton.
+ #
+ # * <tt>login_field</tt> - default: :login, :username, or :email, depending on which column is present, if none are present defaults to :login
+ # The name of the field used for logging in. Only specify if you aren't using any of the defaults.
+ #
+ # * <tt>login_field_type</tt> - default: options[:login_field] == :email ? :email : :login,
+ # Tells authlogic how to validation the field, what regex to use, etc. If the field name is email it will automatically use :email,
+ # otherwise it uses :login.
+ #
+ # * <tt>login_field_regex</tt> - default: if :login_field_type is :email then typical email regex, otherwise typical login regex.
+ # This is used in validates_format_of for the :login_field.
+ #
+ # * <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>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,
+ # 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>password_blank_message</tt> - default: "can not be blank",
+ # The error message used when the password is left blank.
+ #
+ # * <tt>confirm_password_did_not_match_message</tt> - default: "did not match",
+ # The error message used when the confirm password does not match the password
+ #
+ # * <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>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
+ # session. This way you have complete control of when sessions expire and you don't have to change passwords to expire sessions. This also
+ # ensures that stale sessions can not be persisted. By stale, I mean sessions that are logged in using an outdated password.
+ #
+ # * <tt>scope</tt> - default: nil,
+ # This scopes validations. If all of your users belong to an account you might want to scope everything to the account. Just pass :account_id
+ #
+ # * <tt>logged_in_timeout</tt> - default: 10.minutes,
+ # This is a nifty feature to tell if a user is logged in or not. It's based on activity. So if the user in inactive longer than
+ # the value passed here they are assumed "logged out". This uses the last_request_at field, this field must be present for this option to take effect.
+ #
+ # * <tt>session_ids</tt> - default: [nil],
+ # The sessions that we want to automatically reset when a user is created or updated so you don't have to worry about this. Set to [] to disable.
+ # Should be an array of ids. See the Authlogic::Session documentation for information on ids. The order is important.
+ # The first id should be your main session, the session they need to log into first. This is generally nil. When you don't specify an id
+ # in your session you are really just inexplicitly saying you want to use the id of nil.
+ module Config
+ def first_column_to_exist(*columns_to_check) # :nodoc:
+ columns_to_check.each { |column_name| return column_name.to_sym if column_names.include?(column_name.to_s) }
+ columns_to_check.first ? columns_to_check.first.to_sym : nil
+ end
+
+ def acts_as_authentic_with_config(options = {})
+ options[:session_class] ||= "#{name}Session"
+ 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
+
+ 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."
+ else
+ options[:login_field_regex] ||= /\A\w[\w\.\-_@ ]+\z/
+ options[:login_field_regex_failed_message] ||= "use only letters, numbers, spaces, and .-_@ please."
+ end
+
+ options[:password_field] ||= :password
+ options[:password_blank_message] ||= "can not be blank"
+ options[:confirm_password_did_not_match_message] ||= "did not match"
+ options[:crypted_password_field] ||= first_column_to_exist(:crypted_password, :encrypted_password, :password_hash, :pw_hash)
+ 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[:logged_in_timeout] ||= 10.minutes
+ options[:logged_in_timeout] = options[:logged_in_timeout].to_i
+ options[:session_ids] ||= [nil]
+
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def self.acts_as_authentic_config
+ #{options.inspect}
+ end
+ end_eval
+
+ acts_as_authentic_without_config(options)
+ end
+ end
+ end
+ end
+ end
+end
+
+ActiveRecord::Base.class_eval do
+ class << self
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Config
+ alias_method_chain :acts_as_authentic, :config
+ end
+end
View
193 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb
@@ -1,135 +1,96 @@
module Authlogic
module ORMAdapters
module ActiveRecordAdapter
- module Credentials # :nodoc:
- def acts_as_authentic_with_credentials(options = {})
- acts_as_authentic_without_credentials(options)
+ module ActsAsAuthentic
+ # = Credentials
+ #
+ # Handles any credential specific code, such as validating the login, encrpyting the password, etc.
+ #
+ # === Class Methods
+ #
+ # * <tt>friendly_unique_token</tt> - returns a random string of 20 alphanumeric characters. Used when resetting the password. This is a more user friendly token then a long Sha512 hash.
+ #
+ # === 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>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
+ def acts_as_authentic_with_credentials(options = {})
+ acts_as_authentic_without_credentials(options)
- # The following helps extract configuration into their specific ORM adapter and allows the Session configuration to set itself based on these values
- class_eval <<-"end_eval", __FILE__, __LINE__
- def self.login_field
- @login_field ||= #{options[:login_field].inspect} ||
- (column_names.include?("login") && :login) ||
- (column_names.include?("username") && :username) ||
- (column_names.include?("email") && :email) ||
- :login
+ # Validations
+ case options[:login_field_type]
+ when :email
+ validates_length_of options[:login_field], :within => 6..100
+ validates_format_of options[:login_field], :with => options[:login_field_regex], :message => options[:login_field_regex_failed_message]
+ else
+ 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
-
- def self.password_field
- @password_field ||= #{options[:password_field].inspect} ||
- (column_names.include?("password") && :password) ||
- (column_names.include?("pass") && :pass) ||
- :password
- end
-
- def self.crypted_password_field
- @crypted_password_field ||= #{options[:crypted_password_field].inspect} ||
- (column_names.include?("crypted_password") && :crypted_password) ||
- (column_names.include?("encrypted_password") && :encrypted_password) ||
- (column_names.include?("password_hash") && :password_hash) ||
- (column_names.include?("pw_hash") && :pw_hash) ||
- :crypted_password
- end
-
- def self.password_salt_field
- @password_salt_field ||= #{options[:password_salt_field].inspect} ||
- (column_names.include?("password_salt") && :password_salt) ||
- (column_names.include?("pw_salt") && :pw_salt) ||
- (column_names.include?("salt") && :salt) ||
- :password_salt
- end
- end_eval
-
- # The following methods allow other focused modules to alter validation behavior, such as openid as an alternate login
- unless respond_to?(:allow_blank_for_login_validations?)
- def self.allow_blank_for_login_validations?
- false
- end
- end
- options[:crypto_provider] ||= CryptoProviders::Sha512
- options[:login_field_type] ||= login_field == :email ? :email : :login
-
- # Validations
- case options[:login_field_type]
- when :email
- validates_length_of login_field, :within => 6..100
- 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_message] ||= "should look like an email address."
- validates_format_of login_field, :with => options[:login_field_regex], :message => options[:login_field_regex_message]
- else
- validates_length_of login_field, :within => 2..100, :allow_blank => true
- options[:login_field_regex] ||= /\A\w[\w\.\-_@ ]+\z/
- options[:login_field_regex_message] ||= "use only letters, numbers, spaces, and .-_@ please."
- validates_format_of login_field, :with => options[:login_field_regex], :message => options[:login_field_regex_message]
- end
+ validates_uniqueness_of options[:login_field], :scope => options[:scope]
+ validate :validate_password
- validates_uniqueness_of login_field, :scope => options[:scope]
- validate :validate_password
+ attr_writer "confirm_#{options[:password_field]}"
+ attr_accessor "tried_to_set_#{options[:password_field]}"
- attr_writer "confirm_#{password_field}"
- attr_accessor "tried_to_set_#{password_field}"
-
- class_eval <<-"end_eval", __FILE__, __LINE__
- def self.crypto_provider
- #{options[:crypto_provider]}
- end
-
- def crypto_provider
- self.class.crypto_provider
- end
-
- def #{password_field}=(pass)
- return if pass.blank?
- self.tried_to_set_#{password_field} = true
- @#{password_field} = pass
- self.#{password_salt_field} = self.class.unique_token
- self.#{crypted_password_field} = crypto_provider.encrypt(@#{password_field} + #{password_salt_field})
- end
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def self.friendly_unique_token
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
+ newpass = ""
+ 1.upto(20) { |i| newpass << chars[rand(chars.size-1)] }
+ newpass
+ end
- def valid_#{password_field}?(attempted_password)
- return false if attempted_password.blank? || #{crypted_password_field}.blank? || #{password_salt_field}.blank?
- attempted_password == #{crypted_password_field} ||
- (crypto_provider.respond_to?(:decrypt) && crypto_provider.decrypt(#{crypted_password_field}) == attempted_password + #{password_salt_field}) ||
- (!crypto_provider.respond_to?(:decrypt) && crypto_provider.encrypt(attempted_password + #{password_salt_field}) == #{crypted_password_field})
- end
+ def #{options[:password_field]}=(pass)
+ return if pass.blank?
+ self.tried_to_set_#{options[:password_field]} = true
+ @#{options[:password_field]} = pass
+ self.#{options[:password_salt_field]} = self.class.unique_token
+ self.#{options[:crypted_password_field]} = #{options[:crypto_provider]}.encrypt(@#{options[:password_field]} + #{options[:password_salt_field]})
+ end
- def #{password_field}; end
- def confirm_#{password_field}; end
+ 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]}.encrypt(attempted_password + #{options[:password_salt_field]}) == #{options[:crypted_password_field]})
+ end
- def reset_#{password_field}
- chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
- newpass = ""
- 1.upto(10) { |i| newpass << chars[rand(chars.size-1)] }
- self.#{password_field} = newpass
- self.confirm_#{password_field} = newpass
- end
- alias_method :randomize_password, :reset_password
+ def #{options[:password_field]}; end
+ def confirm_#{options[:password_field]}; end
- def reset_#{password_field}!
- reset_#{password_field}
- save_without_session_maintenance(false)
- end
- alias_method :randomize_password!, :reset_password!
+ def reset_#{options[:password_field]}
+ friendly_token = self.class.friendly_unique_token
+ self.#{options[:password_field]} = friendly_token
+ self.confirm_#{options[:password_field]} = friendly_token
+ end
+ alias_method :randomize_password, :reset_password
- protected
- def tried_to_set_password?
- tried_to_set_password == true
+ def reset_#{options[:password_field]}!
+ reset_#{options[:password_field]}
+ save_without_session_maintenance(false)
end
+ alias_method :randomize_password!, :reset_password!
+
+ protected
+ def tried_to_set_password?
+ tried_to_set_password == true
+ end
- def validate_password
- if new_record? || tried_to_set_#{password_field}?
- if @#{password_field}.blank?
- errors.add(:#{password_field}, "can not be blank")
- else
- errors.add(:confirm_#{password_field}, "did not match") if @confirm_#{password_field} != @#{password_field}
+ def validate_password
+ if new_record? || tried_to_set_#{options[:password_field]}?
+ if @#{options[:password_field]}.blank?
+ errors.add(:#{options[:password_field]}, #{options[:password_blank_message].inspect})
+ else
+ errors.add(:confirm_#{options[:password_field]}, #{options[:confirm_password_did_not_match_message].inspect}) if @confirm_#{options[:password_field]} != @#{options[:password_field]}
+ end
end
end
- end
- end_eval
+ end_eval
+ end
end
end
end
@@ -138,7 +99,7 @@ def validate_password
ActiveRecord::Base.class_eval do
class << self
- include Authlogic::ORMAdapters::ActiveRecordAdapter::Credentials
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Credentials
alias_method_chain :acts_as_authentic, :credentials
end
end
View
59 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/logged_in.rb
@@ -1,31 +1,42 @@
module Authlogic
module ORMAdapters
module ActiveRecordAdapter
- module LoggedIn # :nodoc:
- def acts_as_authentic_with_logged_in(options = {})
- acts_as_authentic_without_logged_in(options)
+ module ActsAsAuthentic
+ # = Logged In
+ #
+ # Handles all logic determining if a record is logged in or not. This uses the "last_request_at" field, if this field is not present none of this will be available.
+ #
+ # === Named Scopes
+ #
+ # * <tt>logged_in</tt> - returns all records that have a last_request_at value that is > your :logged_in_timeout.ago
+ # * <tt>logged_out</tt> - same as logged in but returns users that are logged out, be careful with using this, this can return a lot of users
+ #
+ # === Instance Methods
+ #
+ # * <tt>logged_in?</tt> - same as the logged_in named scope, but returns true if the record is logged in
+ # * <tt>logged_out?</tt> - opposite of logged_in?
+ module LoggedIn
+ def acts_as_authentic_with_logged_in(options = {})
+ acts_as_authentic_without_logged_in(options)
- options[:logged_in_timeout] ||= 10.minutes
-
- validates_numericality_of :login_count, :only_integer => :true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("login_count")
-
- if column_names.include?("last_request_at")
- named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", options[:logged_in_timeout].ago]} }
- named_scope :logged_out, lambda { {:conditions => ["last_request_at is NULL or last_request_at <= ?", options[:logged_in_timeout].ago]} }
- end
-
- class_eval <<-"end_eval", __FILE__, __LINE__
- def self.logged_in_timeout
- #{options[:logged_in_timeout].to_i}.seconds
+ validates_numericality_of :login_count, :only_integer => :true, :greater_than_or_equal_to => 0, :allow_nil => true if column_names.include?("login_count")
+
+ if column_names.include?("last_request_at")
+ named_scope :logged_in, lambda { {:conditions => ["last_request_at > ?", options[:logged_in_timeout].ago]} }
+ named_scope :logged_out, lambda { {:conditions => ["last_request_at is NULL or last_request_at <= ?", options[:logged_in_timeout].ago]} }
+ end
+
+ if column_names.include?("last_request_at")
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def logged_in?
+ !last_request_at.nil? && last_request_at > #{options[:logged_in_timeout]}.seconds.ago
+ end
+
+ def logged_out?
+ !logged_in?
+ end
+ end_eval
end
- end_eval
-
- if column_names.include?("last_request_at")
- class_eval <<-"end_eval", __FILE__, __LINE__
- def logged_in?
- !last_request_at.nil? && last_request_at > self.class.logged_in_timeout.ago
- end
- end_eval
end
end
end
@@ -35,7 +46,7 @@ def logged_in?
ActiveRecord::Base.class_eval do
class << self
- include Authlogic::ORMAdapters::ActiveRecordAdapter::LoggedIn
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::LoggedIn
alias_method_chain :acts_as_authentic, :logged_in
end
end
View
95 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb
@@ -1,53 +1,60 @@
module Authlogic
module ORMAdapters
module ActiveRecordAdapter
- module Persistence # :nodoc:
- def acts_as_authentic_with_persistence(options = {})
- acts_as_authentic_without_persistence(options)
+ module ActsAsAuthentic
+ # = Persistence
+ #
+ # This is responsible for all record persistence. Basically what your Authlogic session needs to persist the record's session.
+ #
+ # === Class Methods
+ #
+ # * <tt>forget_all!</tt> - resets ALL records remember_token to a unique value, requiring all users to re-login
+ # * <tt>unique_token</tt> - returns a pretty hardcore random token that is finally encrypted with a hash algorithm
+ #
+ # === Instance Methods
+ #
+ # * <tt>forget!</tt> - resets the record's remember_token which requires them to re-login
+ #
+ # === Alias Method Chains
+ #
+ # * <tt>#{options[:password_field]}</tt> - adds in functionality to reset the remember token when the password is changed
+ module Persistence
+ def acts_as_authentic_with_persistence(options = {})
+ acts_as_authentic_without_persistence(options)
- class_eval <<-"end_eval", __FILE__, __LINE__
- def self.remember_token_field
- @remember_token_field ||= #{options[:remember_token_field].inspect} ||
- (column_names.include?("remember_token") && :remember_token) ||
- (column_names.include?("remember_key") && :remember_key) ||
- (column_names.include?("cookie_token") && :cookie_token) ||
- (column_names.include?("cookie_key") && :cookie_key) ||
- :remember_token
- end
- end_eval
-
- validates_uniqueness_of remember_token_field
-
- def unique_token
- # The remember token should be a unique string that is not reversible, which is what a hash is all about
- # if you using encryption this defaults to Sha512.
- token_class = crypto_provider.respond_to?(:decrypt) ? Authlogic::CryptoProviders::Sha512 : crypto_provider
- token_class.encrypt(Time.now.to_s + (1..10).collect{ rand.to_s }.join)
- end
-
- def forget_all!
- # Paginate these to save on memory
- records = nil
- i = 0
- begin
- records = find(:all, :limit => 50, :offset => i)
- records.each { |record| record.forget! }
- i += 50
- end while !records.blank?
- end
+ validates_uniqueness_of options[:remember_token_field]
- class_eval <<-"end_eval", __FILE__, __LINE__
- def forget!
- self.#{remember_token_field} = self.class.unique_token
- save_without_session_maintenance(false)
+ def forget_all!
+ # Paginate these to save on memory
+ records = nil
+ i = 0
+ begin
+ records = find(:all, :limit => 50, :offset => i)
+ records.each { |record| record.forget! }
+ i += 50
+ end while !records.blank?
end
+
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def self.unique_token
+ # The remember token should be a unique string that is not reversible, which is what a hash is all about
+ # if you using encryption this defaults to Sha512.
+ token_class = #{options[:crypto_provider].respond_to?(:decrypt) ? Authlogic::CryptoProviders::Sha512 : options[:crypto_provider]}
+ token_class.encrypt(Time.now.to_s + (1..10).collect{ rand.to_s }.join)
+ end
- def password_with_persistence=(value)
- self.#{remember_token_field} = self.class.unique_token
- self.password_without_persistence = value
- end
- alias_method_chain :password=, :persistence
- end_eval
+ def forget!
+ self.#{options[:remember_token_field]} = self.class.unique_token
+ save_without_session_maintenance(false)
+ end
+
+ def #{options[:password_field]}_with_persistence=(value)
+ self.#{options[:remember_token_field]} = self.class.unique_token
+ self.#{options[:password_field]}_without_persistence = value
+ end
+ alias_method_chain :#{options[:password_field]}=, :persistence
+ end_eval
+ end
end
end
end
@@ -56,7 +63,7 @@ def password_with_persistence=(value)
ActiveRecord::Base.class_eval do
class << self
- include Authlogic::ORMAdapters::ActiveRecordAdapter::Persistence
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Persistence
alias_method_chain :acts_as_authentic, :persistence
end
end
View
118 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/session_maintenance.rb
@@ -1,74 +1,84 @@
module Authlogic
module ORMAdapters
module ActiveRecordAdapter
- module SessionMaintenance # :nodoc:
- def acts_as_authentic_with_session_maintenance(options = {})
- acts_as_authentic_without_session_maintenance(options)
+ module ActsAsAuthentic
+ # = Session Maintenance
+ #
+ # Responsible for maintaining the related session as the record changes. Here is what it does:
+ #
+ # 1. If the user is logged out and creates a new record, they will be logged in as that record
+ # 2. If the user is logged out and changes a record's password, they will be logged in as that record
+ # 3. If a user is logged in and changes his own password, their session will be updated accordingly. This can be done *anywhere*: the my account section, admin area, etc.
+ #
+ # === Instance Methods
+ #
+ # * <tt>save_without_session_maintenance</tt> - allows you to save the record and skip all of the session maintenance completely
+ module SessionMaintenance
+ def acts_as_authentic_with_session_maintenance(options = {})
+ acts_as_authentic_without_session_maintenance(options)
- options[:session_class] ||= "#{name}Session"
- options[:session_ids] ||= [nil]
+ before_save :get_session_information, :if => :update_sessions?
+ after_save :maintain_sessions!, :if => :update_sessions?
- before_save :get_session_information, :if => :update_sessions?
- after_save :maintain_sessions!, :if => :update_sessions?
-
- class_eval <<-"end_eval", __FILE__, __LINE__
- def save_without_session_maintenance(*args)
- @skip_session_maintenance = true
- result = save(*args)
- @skip_session_maintenance = false
- result
- end
-
- protected
- def update_sessions?
- !@skip_session_maintenance && #{options[:session_class]}.activated? && !#{options[:session_ids].inspect}.blank? && #{remember_token_field}_changed?
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def save_without_session_maintenance(*args)
+ @skip_session_maintenance = true
+ result = save(*args)
+ @skip_session_maintenance = false
+ result
end
- def get_session_information
- # Need to determine if we are completely logged out, or logged in as another user
- @_sessions = []
- @_logged_out = true
+ protected
+ def update_sessions?
+ !@skip_session_maintenance && #{options[:session_class]}.activated? && !#{options[:session_ids].inspect}.blank? && #{options[:remember_token_field]}_changed?
+ end
+
+ def get_session_information
+ # Need to determine if we are completely logged out, or logged in as another user
+ @_sessions = []
+ @_logged_out = true
- #{options[:session_ids].inspect}.each do |session_id|
- session = #{options[:session_class]}.find(*[session_id].compact)
- if session
- if !session.record.blank?
- @_logged_out = false
- @_sessions << session if session.record == self
+ #{options[:session_ids].inspect}.each do |session_id|
+ session = #{options[:session_class]}.find(*[session_id].compact)
+ if session
+ if !session.record.blank?
+ @_logged_out = false
+ @_sessions << session if session.record == self
+ end
end
end
end
- end
- def maintain_sessions!
- if @_logged_out
- create_session!
- elsif !@_sessions.blank?
- update_sessions!
+ def maintain_sessions!
+ if @_logged_out
+ create_session!
+ elsif !@_sessions.blank?
+ update_sessions!
+ end
end
- end
- def create_session!
- # We only want to automatically login into the first session, since this is the main session. The other sessions are sessions
- # that need to be created after logging into the main session.
- session_id = #{options[:session_ids].inspect}.first
+ def create_session!
+ # We only want to automatically login into the first session, since this is the main session. The other sessions are sessions
+ # that need to be created after logging into the main session.
+ session_id = #{options[:session_ids].inspect}.first
- # If we are already logged in, ignore this completely. All that we care about is updating ourself.
- next if #{options[:session_class]}.find(*[session_id].compact)
+ # If we are already logged in, ignore this completely. All that we care about is updating ourself.
+ next if #{options[:session_class]}.find(*[session_id].compact)
- # Log me in
- args = [self, session_id].compact
- #{options[:session_class]}.create(*args)
- end
+ # Log me in
+ args = [self, session_id].compact
+ #{options[:session_class]}.create(*args)
+ end
- def update_sessions!
- # We found sessions above, let's update them with the new info
- @_sessions.each do |stale_session|
- stale_session.unauthorized_record = self
- stale_session.save
+ def update_sessions!
+ # We found sessions above, let's update them with the new info
+ @_sessions.each do |stale_session|
+ stale_session.unauthorized_record = self
+ stale_session.save
+ end
end
- end
- end_eval
+ end_eval
+ end
end
end
end
@@ -77,7 +87,7 @@ def update_sessions!
ActiveRecord::Base.class_eval do
class << self
- include Authlogic::ORMAdapters::ActiveRecordAdapter::SessionMaintenance
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::SessionMaintenance
alias_method_chain :acts_as_authentic, :session_maintenance
end
end
View
61 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/single_access.rb
@@ -0,0 +1,61 @@
+module Authlogic
+ module ORMAdapters
+ module ActiveRecordAdapter
+ module ActsAsAuthentic
+ # = Single Access
+ #
+ # Instead of repeating myself here, checkout the README. There is a "Single Access" section in there that goes over this. Keep in mind none of this will be applied if there
+ # is not a single_access_token field supplied in the database.
+ #
+ # === Instance Methods
+ #
+ # * <tt>reset_{options[:single_access_token_field]}</tt> - resets the single access token with the friendly_unique_token
+ # * <tt>reset_{options[:single_access_token_field]}!</tt> - same as above, but saves the record afterwards
+ #
+ # === Alias Method Chains
+ #
+ # * <tt>{options[:password_field]}</tt> - if the :change_single_access_token_with_password is set to true, reset_{options[:single_access_token_field]} will be called when the password changes
+ 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
+ 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
+ end
+ end
+ end
+ end
+end
+
+ActiveRecord::Base.class_eval do
+ class << self
+ include Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::SingleAccess
+ alias_method_chain :acts_as_authentic, :single_access
+ end
+end
View
29 lib/authlogic/session/base.rb
@@ -82,20 +82,18 @@ def controllers
attr_accessor :new_session
attr_reader :record, :unauthorized_record
- attr_writer :authenticating_with, :id
+ attr_writer :authenticating_with, :id, :persisting
# You can initialize a session by doing any of the following:
#
# UserSession.new
# UserSession.new(:login => "login", :password => "password", :remember_me => true)
- # UserSession.new(:openid => "identity url", :remember_me => true)
# UserSession.new(User.first, true)
#
# If a user has more than one session you need to pass an id so that Authlogic knows how to differentiate the sessions. The id MUST be a Symbol.
#
# UserSession.new(:my_id)
# UserSession.new({:login => "login", :password => "password", :remember_me => true}, :my_id)
- # UserSession.new({:openid => "identity url", :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.
@@ -121,7 +119,6 @@ def initialize(*args)
#
# * :password - username and password
# * :unauthorized_record - an actual ActiveRecord object
- # * :openid - OpenID
#
# By default this is :password
def authenticating_with
@@ -234,14 +231,24 @@ def new_session?
new_session != false
end
+ def persisting # :nodoc:
+ return @persisting if defined?(@persisting)
+ @persisting = true
+ end
+
+ # Returns true if the session is being persisted. This is set to false if the session was found by the single_access_token, since logging in via a single access token should not remember the user in the
+ # session or the cookie.
+ def persisting?
+ persisting == true
+ end
+
def remember_me # :nodoc:
- return @remember_me if @set_remember_me
- @remember_me ||= self.class.remember_me
+ return @remember_me if defined?(@remember_me)
+ @remember_me = self.class.remember_me
end
# Accepts a boolean as a flag to remember the session or not. Basically to expire the cookie at the end of the session or keep it for "remember_me_until".
def remember_me=(value)
- @set_remember_me = true
@remember_me = value
end
@@ -290,8 +297,12 @@ def save!
result
end
- # Sometimes you don't want to create a session via credentials (login and password). Maybe you already have the record. Just set this record to this and it will be authenticated when you try to validate
- # the session. Basically this is another form of credentials, you are just skipping username and password validation.
+ # This lets you create a session by passing a single object of whatever you are authenticating. Let's say User. By passing a user object you are vouching for this user and saying you can guarantee
+ # this user is who he says he is, create a session for him.
+ #
+ # This is how persistence works in Authlogic. Authlogic grabs your cookie credentials, finds a user by those credentials, and then vouches for that user and creates a session. You can do this for just about
+ # anything, which comes in handy for those unique authentication methods. Do what you need to do to authenticate the user, guarantee he is who he says he is, then pass the object here. Authlogic will do its
+ # magic: create a session and cookie. Now when the user refreshes their session will be persisted by their session and cookie.
def unauthorized_record=(value)
self.authenticating_with = :unauthorized_record
@unauthorized_record = value
View
117 lib/authlogic/session/config.rb
@@ -8,7 +8,7 @@ def self.included(klass)
# = Session Config
#
- # This deals with configuration for your session. If you are wanting to configure your model please look at Authlogic::ORMAdapters::ActiveRecord::ActsAsAuthentic
+ # This deals with configuration for your session. If you are wanting to configure your model please look at Authlogic::ORMAdapters::ActiveRecordAdapter::ActsAsAuthentic::Config
#
# Configuration for your session is simple. The configuration options are just class methods. Just put this in your config/initializers directory
#
@@ -89,20 +89,6 @@ def find_by_login_method(value = nil)
end
alias_method :find_by_login_method=, :find_by_login_method
- # Once the user confirms their openid Authlogic tries to find the record with that openod. This is the method it called on the record's
- # class to find the record by the openid.
- #
- # * <tt>Default:</tt> "find_by_#{openid_field}"
- # * <tt>Accepts:</tt> Symbol or String
- def find_by_openid_method(value = nil)
- if value.nil?
- read_inheritable_attribute(:find_by_openid_method) || find_by_openid_method("find_by_#{openid_field}")
- else
- write_inheritable_attribute(:find_by_openid_method, value)
- end
- end
- alias_method :find_by_openid_method=, :find_by_openid_method
-
# 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.
#
@@ -110,7 +96,7 @@ def find_by_openid_method(value = nil)
# * <tt>Accepts:</tt> Array, and can only use any of the 3 options above
def find_with(*values)
if values.blank?
- read_inheritable_attribute(:find_with) || find_with(:session, :cookie, :params, :http_auth)
+ read_inheritable_attribute(:find_with) || find_with(:params, :session, :cookie, :http_auth)
else
values.flatten!
write_inheritable_attribute(:find_with, values)
@@ -138,55 +124,23 @@ def last_request_at_threshold(value = nil)
# login with a field called "login" and then find users by email this is compeltely doable. See the find_by_login_method configuration
# option for more details.
#
- # * <tt>Default:</tt> Guesses based on the model columns, tries login, username, and email. If none are present it defaults to login
+ # * <tt>Default:</tt> Uses the configuration option in your model: User.acts_as_authentic_config[:login_field]
# * <tt>Accepts:</tt> Symbol or String
def login_field(value = nil)
if value.nil?
- read_inheritable_attribute(:login_field) || login_field(klass.login_field)
+ read_inheritable_attribute(:login_field) || login_field(klass.acts_as_authentic_config[:login_field])
else
write_inheritable_attribute(:login_field, value)
end
end
alias_method :login_field=, :login_field
- # The name of the method you want Authlogic to create for storing the openid url. 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
- # "openid_url" and then find users by openid this is compeltely doable. See the find_by_openid_method configuration option for
- # more details.
- #
- # * <tt>Default:</tt> Guesses based on the model columns, tries openid, openid_url, identity_url.
- # * <tt>Accepts:</tt> Symbol or String
- def openid_field(value = nil)
- if value.nil?
- read_inheritable_attribute(:openid_field) || openid_field((klass.column_names.include?("openid") && :openid) || (klass.column_names.include?("openid_url") && :openid_url) || (klass.column_names.include?("identity_url") && :identity_url))
- else
- write_inheritable_attribute(:openid_field, value)
- end
- end
- alias_method :openid_field=, :openid_field
-
- # The name of the method you want Authlogic to create for storing the openid url. 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
- # "openid_url" and then find users by openid this is compeltely doable. See the find_by_openid_method configuration option for
- # more details.
+ # 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:
#
- # * <tt>Default:</tt> Guesses based on the model columns, tries openid, openid_url, identity_url.
- # * <tt>Accepts:</tt> Symbol or String
- def openid_file_store_path(value = nil)
- if value.nil?
- read_inheritable_attribute(:openid_file_store_path) || openid_file_store_path((defined?(RAILS_ROOT) && RAILS_ROOT + "/tmp/openids") || (defined?(Merb) && Merb.root + "/tmp/openids"))
- else
- write_inheritable_attribute(:openid_file_store_path, value)
- end
- end
- alias_method :openid_file_store_path=, :openid_file_store_path
-
- # Works exactly like cookie_key, but for params. So a user can login via params just like a cookie or a session. Your URK would look like:
- #
- # http://www.domain.com?user_credentials=fdsfdfd32jfksdjfdksl
+ # http://www.domain.com?user_credentials=my_single_access_key
#
# You can change the "user_credentials" key above with this configuration option. Keep in mind, just like cookie_key, if you supply an id
- # the id will be appended to the front.
+ # the id will be appended to the front. Check out cookie_key for more details. Also checkout the "Single Access / Private Feeds Access" section in the README.
#
# * <tt>Default:</tt> cookie_key
# * <tt>Accepts:</tt> String
@@ -199,13 +153,14 @@ def params_key(value = nil)
end
alias_method :params_key=, :params_key
+
# Works exactly like login_field, but for the password instead.
#
- # * <tt>Default:</tt> Guesses based on the model columns, tries password and pass. If none are present it defaults to password
+ # * <tt>Default:</tt> :password
# * <tt>Accepts:</tt> Symbol or String
def password_field(value = nil)
if value.nil?
- read_inheritable_attribute(:password_field) || password_field(klass.password_field)
+ read_inheritable_attribute(:password_field) || password_field(:password)
else
write_inheritable_attribute(:password_field, value)
end
@@ -242,11 +197,11 @@ def remember_me_for(value = :_read)
# 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> Guesses based on the model columns, tries remember_token, remember_key, cookie_token, and cookie_key. If none are present it defaults to remember_token
+ # * <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.remember_token_field)
+ 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
@@ -266,6 +221,34 @@ def session_key(value = nil)
end
alias_method :session_key=, :session_key
+ # Authentication is allowed via a single access token, but maybe this is something you don't want for your application as a whole. Maybe this is something you only want for specific request types.
+ # Specify a list of allowed request types and single access authentication will only be allowed for the ones you specify. Checkout the "Single Access / Private Feeds Access" section in the README.
+ #
+ # * <tt>Default:</tt> "application/rss+xml", "application/atom+xml"
+ # * <tt>Accepts:</tt> String, or :all to allow single access authentication for any and all request types
+ def single_access_allowed_request_types(*values)
+ if values.blank?
+ read_inheritable_attribute(:single_access_allowed_request_types) || single_access_allowed_request_types("application/rss+xml", "application/atom+xml")
+ else
+ write_inheritable_attribute(:single_access_allowed_request_types, values)
+ end
+ 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}?"
@@ -281,6 +264,10 @@ def verify_password_method(value = nil)
end
module InstanceMethods # :nodoc:
+ def change_single_access_token_with_password?
+ self.class.change_single_access_token_with_password == true
+ end
+
def cookie_key
build_key(self.class.cookie_key)
end
@@ -289,10 +276,6 @@ def find_by_login_method
self.class.find_by_login_method
end
- def find_by_openid_method
- self.class.find_by_openid_method
- end
-
def find_with
self.class.find_with
end
@@ -305,8 +288,8 @@ def login_field
self.class.login_field
end
- def openid_field
- self.class.openid_field
+ def params_allowed_request_types
+ build_key(self.class.params_allowed_request_types)
end
def params_key
@@ -329,6 +312,14 @@ def remember_token_field
def session_key
build_key(self.class.session_key)
end
+
+ def single_access_token_field
+ self.class.single_access_token_field
+ end
+
+ def single_access_allowed_request_types
+ self.class.single_access_allowed_request_types
+ end
def verify_password_method
self.class.verify_password_method
View
4 lib/authlogic/session/cookies.rb
@@ -5,8 +5,8 @@ module Session
# Handles all authentication that deals with cookies, such as persisting a session and saving / destroying a session.
module Cookies
def self.included(klass)
- klass.after_save :save_cookie
- klass.after_destroy :destroy_cookie
+ klass.after_save :save_cookie, :if => :persisting?
+ klass.after_destroy :destroy_cookie, :if => :persisting?
end
# Tries to validate the session from information in the cookie
View
106 lib/authlogic/session/openid.rb
@@ -1,106 +0,0 @@
-module Authlogic
- module Session
- module OpenID
- def self.included(klass)
- klass.class_eval do
- alias_method_chain :initialize, :openid
- alias_method_chain :credentials=, :openid
- alias_method_chain :create_configurable_methods!, :openid
- before_validation :valid_openid?
- attr_accessor :openid_response
- end
- end
-
- def initialize_with_openid(*args)
- initialize_without_openid(*args)
- self.authenticating_with = :openid if openid_verification_complete?
- end
-
- def credentials_with_openid=(values)
- result = self.credentials_without_openid = values
- return result if openid_field.blank? || values.blank? || !values.is_a?(Hash) || values[:openid].blank?
- self.openid = values[:openid]
- result
- end
-
- # Returns true if logging in with openid. Credentials mean username and password.
- def authenticating_with_openid?
- authenticating_with == :openid
- end
-
- def verify_openid?
- authenticating_with_openid? && controller.params[:openid_complete] != "1"
- end
-
- def openid_verified?
- controller.params[:openid_complete] == "1"
- end
-
- def valid_openid?
- return false if openid_field.blank?
-
- if openid_verification_complete?
- case openid_response.status
- when OpenID::Consumer::SUCCESS
-
- when OpenID::Consumer::CANCEL
- errors.add_to_base("OpenID authentication was cancelled.")
- when OpenID::Consumer::FAILURE
- errors.add_to_base("OpenID authentication failed.")
- when OpenID::Consumer::SETUP_NEEDED
- errors.add_to_Base("OpenID authentication needs setup.")
- end
- else
- if authenticating_with_openid?
- if send(openid_field).blank?
- errors.add(openid_field, "can not be blank")
- return false
- end
-
- unless search_for_record(find_by_openid_method, send(openid_field))
- errors.add(openid_field, "did not match any records in our database")
- return false
- end
-
- begin
- self.openid_response = openid_consumer.begin(send(openid_field))
- rescue OpenID::OpenIDError => e
- errors.add("The OpenID identifier #{send(openid_field)} could not be found: #{e}")
- return false
- end
-
- sregreq = OpenID::SReg::Request.new
- # required fields
- #sregreq.request_fields(['email','nickname'], true)
- # optional fields
- #sregreq.request_fields(['dob', 'fullname'], false)
- oidreq.add_extension(sregreq)
- oidreq.return_to_args["openid_complete"] = 1
- end
- end
- end
-
- private
- def create_configurable_methods_with_openid!
- create_configurable_methods_without_openid!
-
- return if openid_field.blank? || respond_to?(openid_field)
-
- if openid_field
- self.class.class_eval <<-"end_eval", __FILE__, __LINE__
- attr_reader :#{openid_field}
-
- def #{openid_field}=(value)
- self.authenticating_with = :openid
- @#{openid_field} = value
- end
- end_eval
- end
- end
-
- def openid_consumer
- @openid_consumer ||= OpenID::Consumer.new(controller.session, OpenID::FilesystemStore.new(openid_file_store_path))
- end
- end
- end
-end
View
15 lib/authlogic/session/params.rb
@@ -3,17 +3,20 @@ module Session
# = Params
#
# Tries to log the user in via params. Think about cookies and sessions. They are just hashes in your controller, so are params. People never
- # look at params as an authentication option, but it can be useful for logging into private feeds. Logging in a user is as simple as:
+ # look at params as an authentication option, but it can be useful for logging into private feeds, etc. Logging in a user is as simple as:
#
- # http://www.domain.com?user_credentials=[insert remember token here]
+ # https://www.domain.com?user_credentials=[insert single access token here]
#
- # The user_credentials is based on the name of your session, the above example assumes UserSession. Also, this can be modified via configuration.
+ # Wait, what is a single access token? It is all explained in the README. Checkout the "Single Access" section in the README. For security reasons, this type of authentication
+ # is ONLY available via single access tokens, you can NOT pass your remember token.
module Params
# Tries to validate the session from information in the params token
def valid_params?
- if params_credentials
- self.unauthorized_record = search_for_record("find_by_#{remember_token_field}", params_credentials)
- return valid?
+ if params_credentials && single_access_token_field && single_access_allowed_request_types.include?(controller.request_content_type)
+ self.unauthorized_record = search_for_record("find_by_#{single_access_token_field}", params_credentials)
+ self.persisting = false
+ return true if valid?
+ self.persisting = true
end
false
View
6 lib/authlogic/session/session.rb
@@ -5,9 +5,9 @@ module Session
# Handles all parts of authentication that deal with sessions. Such as persisting a session and saving / destroy a session.
module Session
def self.included(klass)
- klass.after_save :update_session!
- klass.after_destroy :update_session!
- klass.after_find :update_session!
+ klass.after_save :update_session!, :if => :persisting?
+ klass.after_destroy :update_session!, :if => :persisting?
+ klass.after_find :update_session!, :if => :persisting?
end
# Tries to validate the session from information in the session
View
17 lib/authlogic/testing/shoulda_macros.rb
@@ -1,17 +0,0 @@
-require "test/unit"
-
-module Authlogic
- module Testing
- module ShouldaMacros
- def should_be_authentic(model)
- should "acts as authentic" do
- assert model.respond_to?(:unique_token)
- assert model.respond_to?(:forget_all!)
- assert model.respond_to?(:crypto_provider)
- end
- end
- end
- end
-end
-
-Test::Unit::TestCase.extend Authlogic::Testing::ShouldaMacros
View
2  lib/authlogic/version.rb
@@ -44,7 +44,7 @@ def to_a
MAJOR = 1
MINOR = 1
- TINY = 0
+ TINY = 1
# The current version as a Version instance
CURRENT = new(MAJOR, MINOR, TINY)
View
13 shoulda_macros/authlogic.rb
@@ -0,0 +1,13 @@
+require "test/unit"
+
+module Authlogic
+ module ShouldaMacros
+ def should_be_authentic(model)
+ should "acts as authentic" do
+ assert model.respond_to?(:acts_as_authentic_config)
+ end
+ end
+ end
+end
+
+Test::Unit::TestCase.extend Authlogic::ShouldaMacros
View
4 test/fixtures/employees.yml
@@ -2,7 +2,7 @@ drew:
company: binary_logic
email: dgainor@binarylogic.com
password_salt: <%= salt = Employee.unique_token %>
- crypted_password: "<%= Employee.crypto_provider.encrypt("drewrocks" + salt) %>"
+ crypted_password: "<%= Employee.acts_as_authentic_config[:crypto_provider].encrypt("drewrocks" + salt) %>"
remember_token: 5273d85ed156e9dbd6a7c1438d319ef8c8d41dd24368db6c222de11346c7b11e53ee08d45ecf619b1c1dc91233d22b372482b751b066d0a6f6f9bac42eacaabf
first_name: Drew
last_name: Gainor
@@ -11,7 +11,7 @@ jennifer:
company: logic_over_data
email: jjohnson@logicoverdata.com
password_salt: <%= salt = Employee.unique_token %>
- crypted_password: "<%= Employee.crypto_provider.encrypt("jenniferocks" + salt) %>"
+ crypted_password: "<%= Employee.acts_as_authentic_config[:crypto_provider].encrypt("jenniferocks" + salt) %>"
remember_token: 2be52a8f741ad00056e6f94eb6844d5316527206da7a3a5e3d0e14d19499ef9fe4c47c89b87febb59a2b41a69edfb4733b6b79302040f3de83f297c6991c75a2
first_name: Jennifer
last_name: Johnson
View
2  test/fixtures/users.yml
@@ -5,6 +5,7 @@ ben:
password_salt: <%= salt = User.unique_token %>
crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("benrocks" + salt) %>
remember_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
+ single_access_token: <%= User.friendly_unique_token %>
first_name: Ben
last_name: Johnson
@@ -15,5 +16,6 @@ zack:
password_salt: <%= salt = User.unique_token %>
crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("zackrocks" + salt) %>
remember_token: fd3c2d5ce09ab98e7547d21f1b3dcf9158a9a19b5d3022c0402f32ae197019fce3fdbc6614d7ee57d719bae53bb089e30edc9e5d6153e5bc3afca0ac1d320342
+ single_access_token: <%= User.friendly_unique_token %>
first_name: Zack
last_name: Ham
View
5 test/libs/mock_controller.rb
@@ -1,5 +1,6 @@
class MockController < Authlogic::ControllerAdapters::AbstractAdapter
attr_accessor :http_user, :http_password
+ attr_writer :request_content_type
def initialize
end
@@ -20,6 +21,10 @@ def request
@request ||= MockRequest.new
end
+ def request_content_type
+ @request_content_type ||= "text/html"
+ end
+
def session
@session ||= {}
end
View
217 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_test.rb
@@ -1,217 +0,0 @@
-require File.dirname(__FILE__) + '/../../test_helper.rb'
-
-module ORMAdaptersTests
- module ActiveRecordAdapterTests
- class ActsAsAuthenticTest < ActiveSupport::TestCase
- def test_user_validations
- user = User.new
- assert !user.valid?
- assert user.errors.on(:login)
- assert user.errors.on(:password)
-
- user.login = "a"
- assert !user.valid?
- assert user.errors.on(:login)
- assert user.errors.on(:password)
-
- user.login = "%ben*"
- assert !user.valid?
- assert user.errors.on(:login)
- assert user.errors.on(:password)
-
- user.login = "bjohnson"
- assert !user.valid?
- assert user.errors.on(:login)
- assert user.errors.on(:password)
-
- user.login = "my login"
- assert !user.valid?
- assert !user.errors.on(:login)
- assert user.errors.on(:password)
-
- user.password = "my pass"
- assert !user.valid?
- assert !user.errors.on(:password)
- assert user.errors.on(:confirm_password)
-
- user.confirm_password = "my pizass"
- assert !user.valid?
- assert !user.errors.on(:password)
- assert user.errors.on(:confirm_password)
-
- user.confirm_password = "my pass"
- assert user.valid?
- end
-
- def test_employee_validations
- employee = Employee.new
- employee.password = "pass"
- employee.confirm_password = "pass"
-
- assert !employee.valid?
- assert employee.errors.on(:email)
-
- employee.email = "fdsf"
- assert !employee.valid?
- assert employee.errors.on(:email)
-
- employee.email = "fake@email.fake"
- assert !employee.valid?
- assert employee.errors.on(:email)
-
- employee.email = "notfake@email.com"
- assert employee.valid?
- end
-
- def test_named_scopes
- assert_equal 0, User.logged_in.count
- assert_equal User.count, User.logged_out.count
- http_basic_auth_for(users(:ben)) { UserSession.find }
- assert_equal 1, User.logged_in.count
- assert_equal User.count - 1, User.logged_out.count
- end
-
- def test_unique_token
- assert_equal 128, User.unique_token.length
- assert_equal 128, Employee.unique_token.length # make sure encryptions use hashes also
-
- unique_tokens = []
- 1000.times { unique_tokens << User.unique_token }
- unique_tokens.uniq!
-
- assert_equal 1000, unique_tokens.size
- end
-
- def test_crypto_provider
- assert_equal Authlogic::CryptoProviders::Sha512, User.crypto_provider
- assert_equal AES128CryptoProvider, Employee.crypto_provider
- end
-
- def test_forget_all
- http_basic_auth_for(users(:ben)) { UserSession.find }
- http_basic_auth_for(users(:zack)) { UserSession.find(:ziggity_zack) }
- assert UserSession.find
- assert UserSession.find(:ziggity_zack)
- User.forget_all!
- assert !UserSession.find
- assert !UserSession.find(:ziggity_zack)
- end
-
- def test_logged_in
- ben = users(:ben)
- assert !ben.logged_in?
- http_basic_auth_for(ben) { UserSession.find }
- assert ben.reload.logged_in?
- end
-
- def test_password
- user = User.new
- user.password = "sillywilly"
- assert user.crypted_password
- assert user.password_salt
- assert user.remember_token
- assert_equal true, user.tried_to_set_password
- assert_nil user.password
-
- employee = Employee.new
- employee.password = "awesome"
- assert employee.crypted_password
- assert employee.remember_token
- assert_equal true, employee.tried_to_set_password
- assert_nil employee.password
- end
-
- def test_valid_password
- ben = users(:ben)
- assert ben.valid_password?("benrocks")
- assert ben.valid_password?(ben.crypted_password)
-
- drew = employees(:drew)
- assert drew.valid_password?("drewrocks")
- assert drew.valid_password?(drew.crypted_password)
- end
-
- def test_forget
- ben = users(:ben)
- zack = users(:zack)
- http_basic_auth_for(ben) { UserSession.find }
- http_basic_auth_for(zack) { UserSession.find(:ziggity_zack) }
-
- assert ben.reload.logged_in?
- assert zack.reload.logged_in?
-
- ben.forget!
-
- assert !UserSession.find
- assert UserSession.find(:ziggity_zack)
- end
-
- def test_reset_password
- ben = users(:ben)
- UserSession.create(ben)
- old_password = ben.crypted_password
- old_salt = ben.password_salt
- old_remember_token = ben.remember_token
- ben.reset_password!
- ben.reload
- assert_not_equal old_password, ben.crypted_password
- assert_not_equal old_salt, ben.password_salt
- assert_not_equal old_remember_token, ben.remember_token
- assert !UserSession.find
- end
-
- def test_login_after_create
- assert User.create(:login => "awesome", :password => "saweet", :confirm_password => "saweet")
- assert UserSession.find
- end
-
- def test_update_session_after_password_modify
- ben = users(:ben)
- UserSession.create(ben)
- old_session_key = @controller.session["user_credentials"]
- old_cookie_key = @controller.cookies["user_credentials"]
- ben.password = "newpass"
- ben.confirm_password = "newpass"
- ben.save
- assert @controller.session["user_credentials"]
- assert @controller.cookies["user_credentials"]
- assert_not_equal @controller.session["user_credentials"], old_session_key
- assert_not_equal @controller.cookies["user_credentials"], old_cookie_key
- end
-
- def test_no_session_update_after_modify
- ben = users(:ben)
- UserSession.create(ben)
- old_session_key = @controller.session["user_credentials"]
- old_cookie_key = @controller.cookies["user_credentials"]
- ben.first_name = "Ben"
- ben.save
- assert_equal @controller.session["user_credentials"], old_session_key
- assert_equal @controller.cookies["user_credentials"], old_cookie_key
- end
-
- def test_updating_other_user
- ben = users(:ben)
- UserSession.create(ben)
- old_session_key = @controller.session["user_credentials"]
- old_cookie_key = @controller.cookies["user_credentials"]
- zack = users(:zack)
- zack.password = "newpass"
- zack.confirm_password = "newpass"
- zack.save
- assert_equal @controller.session["user_credentials"], old_session_key
- assert_equal @controller.cookies["user_credentials"], old_cookie_key
- end
-
- def test_resetting_password_when_logged_out
- ben = users(:ben)
- assert !UserSession.find
- ben.password = "newpass"
- ben.confirm_password = "newpass"
- ben.save
- assert UserSession.find
- assert_equal ben, UserSession.find.record
- end
- end
- end
-end
View
36 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/config_test.rb
@@ -0,0 +1,36 @@
+require File.dirname(__FILE__) + '/../../../test_helper.rb'
+
+module ORMAdaptersTests
+ module ActiveRecordAdapterTests
+ module ActsAsAuthenticTests
+ class ConfigTest < ActiveSupport::TestCase
+ def test_first_column_to_exist
+ assert_equal :login, User.first_column_to_exist(:login, :crypted_password)
+ assert_equal nil, User.first_column_to_exist(nil, :unknown)
+ assert_equal :login, User.first_column_to_exist(:unknown, :login)
+ end
+
+ def test_acts_as_authentic_config
+ default_config = {
+ :confirm_password_did_not_match_message => "did not match",
+ :single_access_token_field => :single_access_token,
+ :login_field_regex => /\A\w[\w\.\-_@ ]+\z/,
+ :session_ids => [nil],
+ :login_field_regex_failed_message => "use only letters, numbers, spaces, and .-_@ please.",
+ :remember_token_field => :remember_token,
+ :password_field => :password,
+ :logged_in_timeout => 600,
+ :password_salt_field => :password_salt,
+ :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
+ }
+ assert_equal default_config, User.acts_as_authentic_config
+ end
+ end
+ end
+ end
+end
View
129 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/credentials_test.rb
@@ -0,0 +1,129 @@
+require File.dirname(__FILE__) + '/../../../test_helper.rb'
+
+module ORMAdaptersTests
+ module ActiveRecordAdapterTests
+ module ActsAsAuthenticTests
+ class CredentialsTest < ActiveSupport::TestCase
+ def test_user_validations
+ user = User.new
+ assert !user.valid?
+ assert user.errors.on(:login)
+ assert user.errors.on(:password)
+
+ user.login = "a"
+ assert !user.valid?
+ assert user.errors.on(:login)
+ assert user.errors.on(:password)
+
+ user.login = "%ben*"
+ assert !user.valid?
+ assert user.errors.on(:login)
+ assert user.errors.on(:password)
+
+ user.login = "bjohnson"
+ assert !user.valid?
+ assert user.errors.on(:login)
+ assert user.errors.on(:password)
+
+ user.login = "my login"
+ assert !user.valid?
+ assert !user.errors.on(:login)
+ assert user.errors.on(:password)
+
+ user.password = "my pass"
+ assert !user.valid?
+ assert !user.errors.on(:password)
+ assert user.errors.on(:confirm_password)
+
+ user.confirm_password = "my pizass"
+ assert !user.valid?
+ assert !user.errors.on(:password)
+ assert user.errors.on(:confirm_password)
+
+ user.confirm_password = "my pass"
+ assert user.valid?
+ end
+
+ def test_employee_validations
+ employee = Employee.new
+ employee.password = "pass"
+ employee.confirm_password = "pass"
+
+ assert !employee.valid?
+ assert employee.errors.on(:email)
+
+ employee.email = "fdsf"
+ assert !employee.valid?
+ assert employee.errors.on(:email)
+
+ employee.email = "fake@email.fake"
+ assert !employee.valid?
+ assert employee.errors.on(:email)
+
+ employee.email = "notfake@email.com"
+ assert employee.valid?
+ end
+
+ def test_friendly_unique_token
+ assert_equal 20, User.friendly_unique_token.length
+ assert_equal 20, Employee.friendly_unique_token.length # make sure encryptions use hashes also
+
+ unique_tokens = []
+ 1000.times { unique_tokens << User.friendly_unique_token }
+ unique_tokens.uniq!
+
+ assert_equal 1000, unique_tokens.size
+ end
+
+ def test_password
+ user = User.new
+ user.password = "sillywilly"
+ assert user.crypted_password
+ assert user.password_salt
+ assert user.remember_token
+ assert_equal true, user.tried_to_set_password
+ assert_nil user.password
+
+ employee = Employee.new
+ employee.password = "awesome"
+ assert employee.crypted_password
+ assert employee.remember_token
+ assert_equal true, employee.tried_to_set_password
+ assert_nil employee.password
+ end
+
+ def test_valid_password
+ ben = users(:ben)
+ assert ben.valid_password?("benrocks")
+ assert ben.valid_password?(ben.crypted_password)
+
+ drew = employees(:drew)
+ assert drew.valid_password?("drewrocks")
+ assert drew.valid_password?(drew.crypted_password)
+ end
+
+ def test_reset_password
+ ben = users(:ben)
+ UserSession.create(ben)
+ assert UserSession.find
+
+ old_password = ben.crypted_password
+ old_salt = ben.password_salt
+ old_remember_token = ben.remember_token
+ ben.reset_password
+ assert_not_equal old_password, ben.crypted_password
+ assert_not_equal old_salt, ben.password_salt
+ assert_not_equal old_remember_token, ben.remember_token
+ assert UserSession.find
+
+ ben.reset_password!
+ ben.reload
+ assert_not_equal old_password, ben.crypted_password
+ assert_not_equal old_salt, ben.password_salt
+ assert_not_equal old_remember_token, ben.remember_token
+ assert !UserSession.find
+ end
+ end
+ end
+ end
+end
View
24 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/logged_in_test.rb
@@ -0,0 +1,24 @@
+require File.dirname(__FILE__) + '/../../../test_helper.rb'
+
+module ORMAdaptersTests
+ module ActiveRecordAdapterTests
+ module ActsAsAuthenticTests
+ class LoggedInTest < ActiveSupport::TestCase
+ def test_named_scopes
+ assert_equal 0, User.logged_in.count
+ assert_equal User.count, User.logged_out.count
+ http_basic_auth_for(users(:ben)) { UserSession.find }
+ assert_equal 1, User.logged_in.count
+ assert_equal User.count - 1, User.logged_out.count
+ end
+
+ def test_logged_in
+ ben = users(:ben)
+ assert !ben.logged_in?
+ assert ben.update_attribute(:last_request_at, Time.now)
+ assert ben.logged_in?
+ end
+ end
+ end
+ end
+end
View
45 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/persistence_test.rb
@@ -0,0 +1,45 @@
+require File.dirname(__FILE__) + '/../../../test_helper.rb'
+
+module ORMAdaptersTests
+ module ActiveRecordAdapterTests
+ module ActsAsAuthenticTests
+ class PersistenceTest < ActiveSupport::TestCase
+ def test_unique_token
+ assert_equal 128, User.unique_token.length
+ assert_equal 128, Employee.unique_token.length # make sure encryptions use hashes also
+
+ unique_tokens = []
+ 1000.times { unique_tokens << User.unique_token }
+ unique_tokens.uniq!
+
+ assert_equal 1000, unique_tokens.size
+ end
+
+ def test_forget_all
+ http_basic_auth_for(users(:ben)) { UserSession.find }
+ http_basic_auth_for(users(:zack)) { UserSession.find(:ziggity_zack) }
+ assert UserSession.find
+ assert UserSession.find(:ziggity_zack)
+ User.forget_all!
+ assert !UserSession.find
+ assert !UserSession.find(:ziggity_zack)
+ end
+
+ def test_forget
+ ben = users(:ben)
+ zack = users(:zack)
+ http_basic_auth_for(ben) { UserSession.find }
+ http_basic_auth_for(zack) { UserSession.find(:ziggity_zack) }
+
+ assert ben.reload.logged_in?
+ assert zack.reload.logged_in?
+
+ ben.forget!
+
+ assert !UserSession.find
+ assert UserSession.find(:ziggity_zack)
+ end
+ end
+ end
+ end
+end
View
62 test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_tests/session_maintenance_test.rb
@@ -0,0 +1,62 @@
+require File.dirname(__FILE__) + '/../../../test_helper.rb'
+
+module ORMAdaptersTests
+ module ActiveRecordAdapterTests
+ module ActsAsAuthenticTests
+ class SessionMaintenanceTest < ActiveSupport::TestCase
+ def test_login_after_create
+ assert User.create(:login => "awesome", :password => "saweet", :confirm_password => "saweet")
+ assert UserSession.find
+ end
+
+ def test_update_session_after_password_modify
+ ben = users(:ben)
+ UserSession.create(ben)
+ old_session_key = @controller.session["user_credentials"]
+ old_cookie_key = @controller.cookies["user_credentials"]
+ ben.password = "newpass"
+ ben.confirm_password = "newpass"
+ ben.save
+ assert @controller.session["user_credentials"]
+ assert @controller.cookies["user_credentials"]
+ assert_not_equal @controller.session["user_credentials"], old_session_key
+ assert_not_equal @controller.cookies["user_credentials"], old_cookie_key
+ end
+
+ def test_no_session_update_after_modify
+ ben = users(:ben)
+ UserSession.create(ben)
+ old_session_key = @controller.session["user_credentials"]
+ old_cookie_key = @controller.cookies["user_credentials"]
+ ben.first_name = "Ben"
+ ben.save
+ assert_equal @controller.session["user_credentials"], old_session_key
+ assert_equal @controller.cookies["user_credentials"], old_cookie_key
+ end
+
+ def test_updating_other_user
+ ben = users(:ben)
+ UserSession.create(ben)
</