Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added Sha1 crypto provider to help with the restful_authentication tr…

…ansition
  • Loading branch information...
commit 93a47874c7b02b4c7ade97a92db37c2fca1f1a19 1 parent 9bca67d
@binarylogic authored
View
3  CHANGELOG.rdoc
@@ -7,6 +7,9 @@
* 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.
+* 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
== 1.0.0 released 2008-11-05
View
11 README.rdoc
@@ -71,13 +71,14 @@ Authlogic makes this a reality. This is just the tip of the ice berg. Keep readi
== Install and use
-Install the gem / plugin
+Install the gem / plugin (recommended)
$ sudo gem install authlogic
- $ cd vendor/plugins
- $ sudo gem unpack authlogic
-Or as a plugin
+ # config/environment.rb
+ config.gem :authlogic
+
+Or as a plugin (for older versions of rails)
script/plugin install git://github.com/binarylogic/authlogic.git
@@ -111,8 +112,6 @@ Make sure you have a model that you will be authenticating with. For this exampl
acts_as_authentic # for options see documentation: Authgasm::ActsAsAuthentic::ClassMethods
end
-The options for acts_as_authentic are based on the UserSession configuration. So if you specified configuration for your UserSession model you should not have to specify any options for acts_as_authentic, unless you want them to be different.
-
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.
== Magic Columns
View
7 lib/authlogic.rb
@@ -6,7 +6,8 @@
require File.dirname(__FILE__) + "/authlogic/controller_adapters/rails_adapter" if defined?(Rails)
require File.dirname(__FILE__) + "/authlogic/controller_adapters/merb_adapter" if defined?(Merb)
-require File.dirname(__FILE__) + "/authlogic/sha512_crypto_provider"
+require File.dirname(__FILE__) + "/authlogic/crypto_providers/sha1"
+require File.dirname(__FILE__) + "/authlogic/crypto_providers/sha512"
if defined?(ActiveRecord)
require File.dirname(__FILE__) + "/authlogic/orm_adapters/active_record_adapter/acts_as_authentic"
@@ -21,7 +22,9 @@
require File.dirname(__FILE__) + "/authlogic/session/active_record_trickery"
require File.dirname(__FILE__) + "/authlogic/session/callbacks"
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/session"
require File.dirname(__FILE__) + "/authlogic/session/scopes"
require File.dirname(__FILE__) + "/authlogic/session/base"
@@ -30,6 +33,8 @@ module Session
class Base
include ActiveRecordTrickery
include Callbacks
+ include Cookies
+ include Session
include Scopes
end
end
View
24 lib/authlogic/crypto_providers/sha1.rb
@@ -0,0 +1,24 @@
+require "digest/sha1"
+
+module Authlogic
+ module CryptoProviders
+ # = Sha1
+ #
+ # Uses the Sha1 hash algorithm to encrypt passwords. This class is useful if you are migrating from restful_authentication. This uses the
+ # exact same excryption algorithm with 10 stretches, just like restful_authentication.
+ class Sha1
+ class << self
+ def stretches
+ @stretches ||= 10
+ end
+ attr_writer :stretches
+
+ def encrypt(pass)
+ digest = pass
+ stretches.times { digest = Digest::SHA1.hexdigest(digest) }
+ digest
+ end
+ end
+ end
+ end
+end
View
30 lib/authlogic/crypto_providers/sha512.rb
@@ -0,0 +1,30 @@
+require "digest/sha2"
+
+module Authlogic
+ # = Crypto Providers
+ #
+ # The acts_as_authentic method allows you to pass a :crypto_provider option. This allows you to use any type of encryption you like.
+ # Just create a class with a class level encrypt and decrypt method. The password will be passed as the single parameter to each of these
+ # methods so you can do your magic.
+ #
+ # If you are encrypting via a hash just don't include a decrypt method, since hashes can't be decrypted. Authlogic will notice this adjust accordingly.
+ module CryptoProviders
+ # = Sha512
+ #
+ # Uses the Sha512 hash algorithm to encrypt passwords.
+ class Sha512
+ class << self
+ def stretches
+ @stretches ||= 20
+ end
+ attr_writer :stretches
+
+ def encrypt(pass)
+ digest = pass
+ stretches.times { digest = Digest::SHA512.hexdigest(digest) }
+ digest
+ end
+ end
+ end
+ end
+end
View
2  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic.rb
@@ -31,7 +31,7 @@ module ActsAsAuthentic
# * <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::Sha512CryptoProvider,
+ # * <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,
View
2  lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/credentials.rb
@@ -39,7 +39,7 @@ def self.password_salt_field
end
end_eval
- options[:crypto_provider] ||= Sha512CryptoProvider
+ options[:crypto_provider] ||= CryptoProviders::Sha512
options[:login_field_type] ||= login_field == :email ? :email : :login
# Validations
View
6 lib/authlogic/orm_adapters/active_record_adapter/acts_as_authentic/persistence.rb
@@ -19,8 +19,10 @@ def self.remember_token_field
validates_uniqueness_of remember_token_field
def unique_token
- # Force using the Sha512 because all that we are doing is creating a unique token, a hash is perfect for this
- Authlogic::Sha512CryptoProvider.encrypt(Time.now.to_s + (1..10).collect{ rand.to_s }.join)
+ # 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!
View
65 lib/authlogic/session/base.rb
@@ -140,8 +140,6 @@ def credentials=(values)
def destroy
errors.clear
@record = nil
- controller.cookies.delete cookie_key
- controller.session[session_key] = nil
true
end
@@ -164,13 +162,20 @@ def errors
# 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.
def find_record
- return record if record
+ if record
+ self.new_session = false
+ return record
+ end
+
find_with.each do |find_method|
if send("valid_#{find_method}?")
+ self.new_session = false
+
if record.class.column_names.include?("last_request_at")
record.last_request_at = Time.now
record.save_without_session_maintenance(false)
end
+
return record
end
end
@@ -251,12 +256,6 @@ def remember_me_until
# 4. updates magic fields
def save
if valid?
- update_session!
- controller.cookies[cookie_key] = {
- :value => record.send(remember_token_field),
- :expires => remember_me_until
- }
-
record.login_count = (record.login_count.blank? ? 1 : record.login_count + 1) if record.respond_to?(:login_count)
if record.respond_to?(:current_login_at)
@@ -309,41 +308,7 @@ def valid_http_auth?
if !login.blank? && !password.blank?
send("#{login_field}=", login)
send("#{password_field}=", password)
- result = valid?
- if result
- update_session!
- self.new_session = false
- return result
- end
- end
- end
-
- false
- end
-
- # Tries to validate the session from information in the cookie
- def valid_cookie?
- if cookie_credentials
- self.unauthorized_record = search_for_record("find_by_#{remember_token_field}", cookie_credentials)
- result = valid?
- if result
- update_session!
- self.new_session = false
- return result
- end
- end
-
- false
- end
-
- # Tries to validate the session from information in the session
- def valid_session?
- if session_credentials
- self.unauthorized_record = search_for_record("find_by_#{remember_token_field}", session_credentials)
- result = valid?
- if result
- self.new_session = false
- return result
+ return valid?
end
end
@@ -359,10 +324,6 @@ def controller
self.class.controller
end
- def cookie_credentials
- controller.cookies[cookie_key]
- end
-
def create_configurable_methods!
return if respond_to?(login_field) # already created these methods
@@ -409,14 +370,6 @@ def search_for_record(method, value)
end
end
- def session_credentials
- controller.session[session_key]
- end
-
- def update_session!
- controller.session[session_key] = record && record.send(remember_token_field)
- end
-
def valid_credentials?
unchecked_record = nil
View
18 lib/authlogic/session/callbacks.rb
@@ -4,10 +4,10 @@ module Session
#
# Just like in ActiveRecord you have before_save, before_validation, etc. You have similar callbacks with Authlogic, see all callbacks below.
module Callbacks
- CALLBACKS = %w(before_create after_create before_destroy after_destroy before_save after_save before_update after_update before_validation after_validation)
+ CALLBACKS = %w(before_create after_create before_destroy after_destroy before_find after_find before_save after_save before_update after_update before_validation after_validation)
def self.included(base) #:nodoc:
- [:destroy, :save, :validate].each do |method|
+ [:destroy, :find_record, :save, :validate].each do |method|
base.send :alias_method_chain, method, :callbacks
end
@@ -15,7 +15,7 @@ def self.included(base) #:nodoc:
base.define_callbacks *CALLBACKS
end
- # Run the following callbacks:
+ # Runs the following callbacks:
#
# before_destroy
# destroy
@@ -29,6 +29,18 @@ def destroy_with_callbacks
# Runs the following callbacks:
#
+ # before_find
+ # find_record
+ # after_find # if a record was found
+ def find_record_with_callbacks
+ run_callbacks(:before_find)
+ result = find_record_without_callbacks
+ run_callbacks(:after_find) if result
+ result
+ end
+
+ # Runs the following callbacks:
+ #
# before_save
# before_create # only if new_session? == true
# before_update # only if new_session? == false
View
36 lib/authlogic/session/cookies.rb
@@ -0,0 +1,36 @@
+module Authlogic
+ module Session
+ module Cookies
+ def self.included(klass)
+ klass.after_save :save_cookie
+ klass.after_destroy :destroy_cookie
+ end
+
+ # Tries to validate the session from information in the cookie
+ def valid_cookie?
+ if cookie_credentials
+ self.unauthorized_record = search_for_record("find_by_#{remember_token_field}", cookie_credentials)
+ return valid?
+ end
+
+ false
+ end
+
+ private
+ def cookie_credentials
+ controller.cookies[cookie_key]
+ end
+
+ def save_cookie
+ controller.cookies[cookie_key] = {
+ :value => record.send(remember_token_field),
+ :expires => remember_me_until
+ }
+ end
+
+ def destroy_cookie
+ controller.cookies.delete cookie_key
+ end
+ end
+ end
+end
View
30 lib/authlogic/session/session.rb
@@ -0,0 +1,30 @@
+module Authlogic
+ module Session
+ module Session
+ def self.included(klass)
+ klass.after_save :update_session!
+ klass.after_destroy :update_session!
+ klass.after_find :update_session!
+ end
+
+ # Tries to validate the session from information in the session
+ def valid_session?
+ if session_credentials
+ self.unauthorized_record = search_for_record("find_by_#{remember_token_field}", session_credentials)
+ return valid?
+ end
+
+ false
+ end
+
+ private
+ def session_credentials
+ controller.session[session_key]
+ end
+
+ def update_session!
+ controller.session[session_key] = record && record.send(remember_token_field)
+ end
+ end
+ end
+end
View
18 lib/authlogic/sha512_crypto_provider.rb
@@ -1,18 +0,0 @@
-require "digest/sha2"
-
-module Authlogic
- # = Sha512 Crypto Provider
- #
- # The acts_as_authentic method allows you to pass a :crypto_provider option. This allows you to use any type of encryption you like. Just create a class with a class level encrypt and decrypt method.
- # The password will be passed as the single parameter to each of these methods so you can do your magic.
- #
- # If you are encrypting via a hash just don't include a decrypt method, since hashes can't be decrypted. Authlogic will notice this adjust accordingly.
- class Sha512CryptoProvider
- STRETCHES = 20
- def self.encrypt(pass)
- digest = pass
- STRETCHES.times { digest = Digest::SHA512.hexdigest(digest) }
- digest
- end
- end
-end
View
4 test/fixtures/users.yml
@@ -3,7 +3,7 @@ ben:
projects: web_services
login: bjohnson
password_salt: <%= salt = User.unique_token %>
- crypted_password: <%= Authlogic::Sha512CryptoProvider.encrypt("benrocks" + salt) %>
+ crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("benrocks" + salt) %>
remember_token: 6cde0674657a8a313ce952df979de2830309aa4c11ca65805dd00bfdc65dbcc2f5e36718660a1d2e68c1a08c276d996763985d2f06fd3d076eb7bc4d97b1e317
first_name: Ben
last_name: Johnson
@@ -13,7 +13,7 @@ zack:
projects: web_services
login: zham
password_salt: <%= salt = User.unique_token %>
- crypted_password: <%= Authlogic::Sha512CryptoProvider.encrypt("zackrocks" + salt) %>
+ crypted_password: <%= Authlogic::CryptoProviders::Sha512.encrypt("zackrocks" + salt) %>
remember_token: fd3c2d5ce09ab98e7547d21f1b3dcf9158a9a19b5d3022c0402f32ae197019fce3fdbc6614d7ee57d719bae53bb089e30edc9e5d6153e5bc3afca0ac1d320342
first_name: Zack
last_name: Ham
View
2  test/orm_adapters_tests/active_record_adapter_tests/acts_as_authentic_test.rb
@@ -83,7 +83,7 @@ def test_unique_token
end
def test_crypto_provider
- assert_equal Authlogic::Sha512CryptoProvider, User.crypto_provider
+ assert_equal Authlogic::CryptoProviders::Sha512, User.crypto_provider
assert_equal AES128CryptoProvider, Employee.crypto_provider
end
View
42 test/session_tests/base_test.rb
@@ -69,8 +69,8 @@ def test_scope_method # test_scope is reserved
session.id = :another_id
session.unauthorized_record = ben
assert session.save
- assert_equal ben.remember_token, @controller.session["another_id_some_id_user_credentials"]
- assert_equal ben.remember_token, @controller.cookies["another_id_some_id_user_credentials"]
+ assert_equal "another_id_some_id_user_credentials", session.cookie_key
+ assert_equal "another_id_some_id_user_credentials", session.session_key
end
assert_nil UserSession.scope
@@ -132,13 +132,16 @@ def test_credentials
def test_destroy
ben = users(:ben)
- session = UserSession.create(ben)
- assert session
- assert_equal ben.remember_token, @controller.session["user_credentials"]
- assert_equal ben.remember_token, @controller.cookies["user_credentials"]
- session.destroy
- assert_nil @controller.session["user_credentials"]
- assert_nil @controller.cookies["user_credentials"]
+ session = UserSession.new
+ assert !session.valid?
+ assert !session.errors.empty?
+ assert session.destroy
+ assert session.errors.empty?
+ session.unauthorized_record = ben
+ assert session.save
+ assert session.record
+ assert session.destroy
+ assert !session.record
end
def test_errors
@@ -154,9 +157,8 @@ def test_id
ben = users(:ben)
session = UserSession.new(ben, :my_id)
assert_equal :my_id, session.id
- assert session.save
- assert_equal ben.remember_token, @controller.session["my_id_user_credentials"]
- assert_equal ben.remember_token, @controller.cookies["my_id_user_credentials"]
+ assert_equal "my_id_user_credentials", session.cookie_key
+ assert_equal "my_id_user_credentials", session.session_key
end
def test_inspect
@@ -279,6 +281,7 @@ def test_valid_http_auth
http_basic_auth_for(ben) do
assert session.valid_http_auth?
+ assert session.find_record
assert_equal ben, session.record
assert_equal ben.remember_token, @controller.session["user_credentials"]
assert_equal ben.login, session.login
@@ -287,20 +290,6 @@ def test_valid_http_auth
end
end
- def test_valid_cookie
- ben = users(:ben)
- session = UserSession.new
-
- assert !session.valid_cookie?
-
- set_cookie_for(ben)
- assert session.valid_cookie?
- assert_equal ben, session.record
- assert_equal ben.remember_token, @controller.session["user_credentials"]
- assert_equal ben, session.unauthorized_record
- assert !session.new_session?
- end
-
def test_valid_session
ben = users(:ben)
session = UserSession.new
@@ -309,6 +298,7 @@ def test_valid_session
set_session_for(ben)
assert session.valid_session?
+ assert session.find_record
assert_equal ben, session.record
assert_equal ben.remember_token, @controller.session["user_credentials"]
assert_equal ben, session.unauthorized_record
View
32 test/session_tests/cookies_test.rb
@@ -0,0 +1,32 @@
+require File.dirname(__FILE__) + '/../test_helper.rb'
+
+module SessionTests
+ class CookiesTest < ActiveSupport::TestCase
+ def test_valid_cookie
+ ben = users(:ben)
+ session = UserSession.new
+
+ assert !session.valid_cookie?
+
+ set_cookie_for(ben)
+ assert session.valid_cookie?
+ assert_equal ben, session.unauthorized_record
+ end
+
+ def test_after_save_save_cookie
+ ben = users(:ben)
+ session = UserSession.new(ben)
+ assert session.save
+ assert_equal ben.remember_token, @controller.cookies["user_credentials"]
+ end
+
+ def test_after_destroy_destroy_cookie
+ ben = users(:ben)
+ set_cookie_for(ben)
+ session = UserSession.find
+ assert @controller.cookies["user_credentials"]
+ assert session.destroy
+ assert !@controller.cookies["user_credentials"]
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.