Second version of token_authenticatable reflecting feedback: Nuked all hook-stuff. Should be easy to custom-reset authentication tokens by inheritance.

Signed-off-by: José Valim <jose.valim@gmail.com>
This commit is contained in:
Jonas Grimfelt
2010-01-25 07:29:15 +01:00
committed by José Valim
parent e1440fb430
commit 4878bdb60b
8 changed files with 5 additions and 161 deletions

View File

@@ -18,7 +18,7 @@ class SessionsController < ApplicationController
set_flash_message :notice, :signed_in
sign_in_and_redirect(resource_name, resource, true)
else
set_now_flash_message :alert, (warden.message || :invalid)
set_now_flash_message :alert, warden.message || :invalid
build_resource
render_with_scope :new
end

View File

@@ -132,17 +132,6 @@ module Devise
mattr_accessor :mailer_sender
@@mailer_sender
# Array of known events that should trigger a authentication token reset.
#
# == Valid events:
#
# Warden: :after_set_user, :before_logout
# Authenticatable: :after_changed_password
#
# Note: If set to nil, authentication token will never be reset automatically.
mattr_accessor :reset_authentication_token_on
@@reset_authentication_token_on = nil
# Authentication token params key name of choice. E.g. /users/sign_in?some_key=...
mattr_accessor :authentication_token_param_key
@@authentication_token_param_key = :auth_token

View File

@@ -1,23 +0,0 @@
# After each Warden-sign-in: Ensure authentication token is set - if this is enabled.
Warden::Manager.after_authentication do |record, warden, options|
scope = options[:scope]
puts "#"
if Devise.mappings[scope].try(:token_authenticatable?) && warden.authenticated?(scope)
Devise.reset_authentication_token_on ||= []
if Devise.reset_authentication_token_on.include?(:after_set_user)
record.reset_authentication_token!
end
end
end
# After each Authenticatable-password-change: Ensure authentication token is re-set - if this is enabled.
Devise.after_changed_password do |record, scope|
if Devise.mappings[scope].try(:token_authenticatable?)
Devise.reset_authentication_token_on ||= []
if Devise.reset_authentication_token_on.include?(:after_changed_password)
record.reset_authentication_token!
end
end
end if Devise.respond_to?(:after_changed_password)

View File

@@ -46,53 +46,6 @@ module Devise
end
end
# Creates events/hooks for Devise and for the given module.
#
# Devise::Models.events(Devise::Authenticable, :after_changed_password, :after_timeout_hooks)
#
# The line above creates:
#
# 1) Accessor for each hook holding any callback hooks (see +Devise::Models::config+), or explicit:
#
# Devise::Models.config(Devise::Authenticable, :after_changed_password_hooks, :after_timeout_hooks)
#
# 1) Setup module accessor hook holding any callback hooks (default fallback config that is):
#
# Devise.after_changed_password_hooks = []
# Devise.after_timeout_hooks = []
#
# 2) Callback hooks: +Devise::Authenticable.after_changed_password_hooks+ and +Devise::Authenticable.on_timeout+,
# used in same manner as +Warden::Manager::after_set_user+, etc.
#
# To add the class methods you need to have a module ClassMethods defined
# inside the given class.
#
def self.events(mod, *events)
::Devise::Models.config(mod, *events.collect { |event| :"#{event}_hooks" })
events.each do |event|
::Devise.class_eval <<-METHOD, __FILE__, __LINE__
mattr_accessor :#{event}_hooks
@@#{event}_hooks = []
# Hook for changed password event.
def self.#{event}(options = {}, &block)
raise BlockNotGiven unless block_given?
self.#{event}_hooks << [block, options]
end
METHOD
end
end
# Triggers a named event for a Devise model instance, or more explicitly
# triggers all callback hooks for this event.
#
def self.event!(object, event, *args)
object.class.send(:"#{event}_hooks").each { |hook| hook.first.call(*args[0..hook.first.arity]) }
rescue
# raise "An invalid event was triggered: #{event}. See Devise::Models::events() for usage."
end
# Include the chosen devise modules in your model:
#
# devise :authenticatable, :confirmable, :recoverable

View File

@@ -45,8 +45,6 @@ module Devise
self.password_salt = self.class.encryptor_class.salt
self.encrypted_password = password_digest(@password)
end
::Devise::Models.event!(self, :after_changed_password, self, self.class.name.underscore.to_sym)
end
# Verifies whether an incoming_password (ie from sign in) is the user password.
@@ -83,14 +81,9 @@ module Devise
self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
end
def password_changed?
!valid_password?(params[:old_password])
end
module ClassMethods
Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
Devise::Models.events(self, :after_changed_password)
# Authenticate a user based on configured attribute keys. Returns the
# authenticated user if it's valid or nil. Attributes are by default

View File

@@ -1,5 +1,4 @@
require 'devise/strategies/token_authenticatable'
require 'devise/hooks/token_authenticatable'
module Devise
module Models
@@ -30,13 +29,11 @@ module Devise
end
# Generate authentication token unless already exists.
#
def ensure_authentication_token!
self.reset_authentication_token!(false) if self.authentication_token.blank?
end
# Generate new authentication token (a.k.a. "single access token").
#
def reset_authentication_token!(do_save = true)
self.authentication_token = self.class.authentication_token
self.save if do_save
@@ -44,17 +41,15 @@ module Devise
# Verifies whether an +incoming_authentication_token+ (i.e. from single access URL)
# is the user authentication token.
#
def valid_authentication_token?(incoming_auth_token)
incoming_auth_token.present? && incoming_auth_token == self.authentication_token
end
module ClassMethods
::Devise::Models.config(self, :authentication_token_param_key, :reset_authentication_token_on)
::Devise::Models.config(self, :authentication_token_param_key)
# Authenticate a user based on authentication token.
#
def authenticate_with_token(attributes = {})
token = attributes[::Devise.authentication_token_param_key]
resource = self.find_for_token_authentication(token)
@@ -70,7 +65,8 @@ module Devise
# Find first record based on conditions given (ie by the sign in form).
# Overwrite to add customized conditions, create a join, or maybe use a
# namedscope to filter records while authenticating.
# Example:
#
# == Example:
#
# def self.find_for_token_authentication(token, conditions = {})
# conditions = {:active => true}

View File

@@ -22,6 +22,7 @@ module Devise
private
# Detect authentication token in params: scoped or not.
def authentication_token(scope)
if params[scope]
params[scope][::Devise.authentication_token_param_key]

View File

@@ -46,63 +46,6 @@ class TokenAuthenticationTest < ActionController::IntegrationTest
end
end
test "authentication token should not be reset - if not set to do so if enabled" do
swap Devise, :reset_authentication_token_on => [] do
User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN)
user = create_user
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
# after_set_user-event
user = sign_in_as_existing_user_with_token(:auth_token => VALID_AUTHENTICATION_TOKEN)
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
# after_changed_password-event
user.password = "new_pass"
user.save
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
end
end
test "authentication token should be reset after changed password if enabled" do
swap Devise, :reset_authentication_token_on => [:after_changed_password] do
User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN)
user = create_user
assert_not_blank user.authentication_token
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
# after_set_user-event
user = sign_in_as_existing_user_with_token(:auth_token => VALID_AUTHENTICATION_TOKEN)
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
# after_changed_password-event
User.expects(:authentication_token).returns("*** NEW TOKEN / CHANGED PASSWORD ***")
user.password = "new_pass"
user.save
assert_not_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
end
end
# Problem: Warden::Manager.after_authenticate and/or Warden::Manager.after_set_user ignores my hook. Why? =(
# See: lib/devise/hooks/token_authenticatable.rb
test "authentication token should be reset after logging in if enabled" do
swap Devise, :reset_authentication_token_on => [:after_set_user] do
User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN)
user = create_user
assert_not_blank user.authentication_token
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
# after_changed_password-event
user.password = "new_pass"
user.save
assert_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
# FIXME: after_set_user-event
User.expects(:authentication_token).returns("*** NEW TOKEN / SIGN IN ***")
user = sign_in_as_existing_user_with_token(:auth_token => VALID_AUTHENTICATION_TOKEN)
assert_not_equal VALID_AUTHENTICATION_TOKEN, user.authentication_token
end
end
private
def sign_in_as_new_user_with_token(options = {}, &block)
@@ -115,12 +58,4 @@ class TokenAuthenticationTest < ActionController::IntegrationTest
user
end
def sign_in_as_existing_user_with_token(options = {}, &block)
options[:auth_token_key] ||= Devise.authentication_token_param_key
options[:auth_token] ||= VALID_AUTHENTICATION_TOKEN
user = User.authenticate_with_token(options[:auth_token_key].to_sym => options[:auth_token])
yield if block_given?
user
end
end