From 3781a0f47b03d137f9dfa00d6509825e43f79466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 2 Feb 2010 13:21:00 +0100 Subject: [PATCH] Tidy up token authentication implementation. --- CHANGELOG.rdoc | 4 ++ app/controllers/sessions_controller.rb | 2 +- generators/devise_install/templates/devise.rb | 4 ++ lib/devise.rb | 4 +- lib/devise/models/token_authenticatable.rb | 42 +++++++++++-------- .../strategies/token_authenticatable.rb | 4 +- .../integration/token_authenticatable_test.rb | 36 +++++++--------- test/models/token_authenticatable_test.rb | 18 +++++--- 8 files changed, 64 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index a2aa1b5e..1b915ab4 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -1,3 +1,7 @@ +* enhancements + * Added gemspec to repo + * Added token authenticatable (by github.com/grimen) + == 0.9.1 * bug fix diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 1ea0485b..3019a07d 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -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 diff --git a/generators/devise_install/templates/devise.rb b/generators/devise_install/templates/devise.rb index ea2d6adc..07a81df2 100644 --- a/generators/devise_install/templates/devise.rb +++ b/generators/devise_install/templates/devise.rb @@ -53,6 +53,10 @@ Devise.setup do |config| # Time interval to unlock the account if :time is enabled as unlock_strategy. # config.unlock_in = 1.hour + # ==> Configuration for :token_authenticatable + # Defines name of the authentication token params key + # config.token_authentication_key = :auth_token + # ==> General configuration # Load and configure the ORM. Supports :active_record (default), :mongo_mapper # (requires mongo_ext installed) and :data_mapper (experimental). diff --git a/lib/devise.rb b/lib/devise.rb index 13b69839..b0fcea09 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -133,8 +133,8 @@ module Devise @@mailer_sender # 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 + mattr_accessor :token_authentication_key + @@token_authentication_key = :auth_token class << self # Default way to setup Devise. Run script/generate devise_install to create diff --git a/lib/devise/models/token_authenticatable.rb b/lib/devise/models/token_authenticatable.rb index 5a0799f6..a97e3544 100644 --- a/lib/devise/models/token_authenticatable.rb +++ b/lib/devise/models/token_authenticatable.rb @@ -10,9 +10,7 @@ module Devise # You can overwrite configuration values by setting in globally in Devise (+Devise.setup+), # using devise method, or overwriting the respective instance method. # - # +authentication_token_param_key+ - Defines name of the authentication token params key. E.g. /users/sign_in?some_key=... - # - # +reset_authentication_token_on+ - Defines which callback hooks that should trigger a authentication token reset. + # +token_authentication_key+ - Defines name of the authentication token params key. E.g. /users/sign_in?some_key=... # # == Examples: # @@ -23,20 +21,29 @@ module Devise def self.included(base) base.class_eval do extend ClassMethods - - before_save :ensure_authentication_token! + before_save :ensure_authentication_token end end - # Generate authentication token unless already exists. - def ensure_authentication_token! - self.reset_authentication_token!(false) if self.authentication_token.blank? + # Generate new authentication token (a.k.a. "single access token"). + def reset_authentication_token + self.authentication_token = self.class.authentication_token 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 + # Generate new authentication token and save the record. + def reset_authentication_token! + reset_authentication_token + self.save + end + + # Generate authentication token unless already exists. + def ensure_authentication_token + self.reset_authentication_token if self.authentication_token.blank? + end + + # Generate authentication token unless already exists and save the record. + def ensure_authentication_token! + self.reset_authentication_token! if self.authentication_token.blank? end # Verifies whether an +incoming_authentication_token+ (i.e. from single access URL) @@ -46,12 +53,11 @@ module Devise end module ClassMethods - - ::Devise::Models.config(self, :authentication_token_param_key) + ::Devise::Models.config(self, :token_authentication_key) # Authenticate a user based on authentication token. - def authenticate_with_token(attributes = {}) - token = attributes[::Devise.authentication_token_param_key] + def authenticate_with_token(attributes) + token = attributes[self.token_authentication_key] resource = self.find_for_token_authentication(token) resource if resource.try(:valid_authentication_token?, token) end @@ -73,8 +79,8 @@ module Devise # self.find_by_authentication_token(token, :conditions => conditions) # end # - def find_for_token_authentication(token, conditions = {}) - self.find_by_authentication_token(token, :conditions => conditions) + def find_for_token_authentication(token) + self.find(:first, :conditions => { :authentication_token => token}) end end diff --git a/lib/devise/strategies/token_authenticatable.rb b/lib/devise/strategies/token_authenticatable.rb index 19ee139f..1eea4d08 100644 --- a/lib/devise/strategies/token_authenticatable.rb +++ b/lib/devise/strategies/token_authenticatable.rb @@ -25,9 +25,9 @@ module Devise # Detect authentication token in params: scoped or not. def authentication_token(scope) if params[scope] - params[scope][::Devise.authentication_token_param_key] + params[scope][mapping.to.token_authentication_key] else - params[::Devise.authentication_token_param_key] + params[mapping.to.token_authentication_key] end end diff --git a/test/integration/token_authenticatable_test.rb b/test/integration/token_authenticatable_test.rb index 3590167a..ea878267 100644 --- a/test/integration/token_authenticatable_test.rb +++ b/test/integration/token_authenticatable_test.rb @@ -3,8 +3,8 @@ require 'test/test_helper' class TokenAuthenticationTest < ActionController::IntegrationTest test 'sign in user should authenticate with valid authentication token and proper authentication token key' do - swap Devise, :authentication_token_param_key => :secret_token do - sign_in_as_new_user_with_token(:auth_token_key => :secret_token, :auth_token => VALID_AUTHENTICATION_TOKEN) + swap Devise, :token_authentication_key => :secret_token do + sign_in_as_new_user_with_token(:auth_token_key => :secret_token) assert_response :success assert_template 'users/index' @@ -14,30 +14,22 @@ class TokenAuthenticationTest < ActionController::IntegrationTest end test 'user signing in with valid authentication token - but improper authentication token key - return to sign in form with error message' do - # FIXME: For some reason I18n value is not respected. Always render defalt one. =S - # store_translations :en, :devise => {:sessions => {:unauthenticated => 'Ouch!'}} do - # assert 'Ouch!', I18n.t('devise.sessions.unauthenticated') # for paranoia + swap Devise, :token_authentication_key => :donald_duck_token do + sign_in_as_new_user_with_token(:auth_token_key => :secret_token) + assert_redirected_to new_user_session_path(:unauthenticated => true) + follow_redirect! - swap Devise, :authentication_token_param_key => :donald_duck_token do - sign_in_as_new_user_with_token(:auth_token_key => :secret_token, :auth_token => VALID_AUTHENTICATION_TOKEN) - - assert_redirected_to new_user_session_path(:unauthenticated => true) - follow_redirect! - - # assert_contain 'Ouch!' - assert_contain 'Sign in' - assert_not warden.authenticated?(:user) - end - # end + assert_contain 'You need to sign in or sign up before continuing' + assert_contain 'Sign in' + assert_not warden.authenticated?(:user) + end end test 'user signing in with invalid authentication token should return to sign in form with error message' do store_translations :en, :devise => {:sessions => {:invalid_token => 'LOL, that was not a single character correct.'}} do sign_in_as_new_user_with_token(:auth_token => '*** INVALID TOKEN ***') - assert_redirected_to new_user_session_path(:invalid_token => true) follow_redirect! - assert_equal users_path(Devise.authentication_token_param_key => '*** INVALID TOKEN ***'), session[:"user.return_to"] assert_response :success assert_contain 'LOL, that was not a single character correct.' @@ -49,12 +41,14 @@ class TokenAuthenticationTest < ActionController::IntegrationTest private def sign_in_as_new_user_with_token(options = {}, &block) - options[:auth_token_key] ||= Devise.authentication_token_param_key + options[:auth_token_key] ||= Devise.token_authentication_key + options[:auth_token] ||= VALID_AUTHENTICATION_TOKEN + user = create_user(options) user.authentication_token = VALID_AUTHENTICATION_TOKEN user.save - visit users_path(options[:auth_token_key].to_sym => (options[:auth_token] || VALID_AUTHENTICATION_TOKEN)) - yield if block_given? + + visit users_path(options[:auth_token_key].to_sym => options[:auth_token]) user end diff --git a/test/models/token_authenticatable_test.rb b/test/models/token_authenticatable_test.rb index afab0a5c..2c8d75b7 100644 --- a/test/models/token_authenticatable_test.rb +++ b/test/models/token_authenticatable_test.rb @@ -11,14 +11,20 @@ class TokenAuthenticatableTest < ActiveSupport::TestCase test 'should reset authentication token' do user = new_user - - user.reset_authentication_token!(false) + user.reset_authentication_token previous_token = user.authentication_token - - user.reset_authentication_token!(false) + user.reset_authentication_token assert_not_equal previous_token, user.authentication_token end + test 'should ensure authentication token' do + user = new_user + user.ensure_authentication_token + previous_token = user.authentication_token + user.ensure_authentication_token + assert_equal previous_token, user.authentication_token + end + test 'should test for a valid authentication token' do User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN) user = create_user @@ -29,7 +35,7 @@ class TokenAuthenticatableTest < ActiveSupport::TestCase test 'should authenticate a valid user with authentication token and return it' do User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN) user = create_user - User.any_instance.stubs(:confirmed?).returns(true) + user.confirm! authenticated_user = User.authenticate_with_token(:auth_token => user.authentication_token) assert_equal authenticated_user, user end @@ -37,7 +43,7 @@ class TokenAuthenticatableTest < ActiveSupport::TestCase test 'should return nil when authenticating an invalid user by authentication token' do User.expects(:authentication_token).returns(VALID_AUTHENTICATION_TOKEN) user = create_user - User.any_instance.stubs(:confirmed?).returns(true) + user.confirm! authenticated_user = User.authenticate_with_token(:auth_token => user.authentication_token.reverse) assert_nil authenticated_user end