diff --git a/TODO b/TODO index 2d2792e6..86b8794f 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,7 @@ * Clear perishable_token in :confirmable and :recoverable * Remove perishable token and create attributes for confirmation_token and reset_password_token * Add confirmation_sent_at for confirmable -* Store session[:return_to] in session -* Use sign_in and sign_out in SessionsController -* Use path_names in routes * Add remember me (with customizable time frame) * Add confirmable filters @@ -33,3 +30,6 @@ * devise :authenticable, :confirmable, :recoverable * Allow multiple models per controller * Add mappings +* Use sign_in and sign_out in SessionsController +* Use path_names in routes +* Store session[:return_to] in session diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index b8af4261..1bf721e6 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -11,7 +11,7 @@ class SessionsController < ApplicationController def create if sign_in(resource_name) set_flash_message :success, :signed_in - redirect_to root_path + redirect_back_or_to root_path else unauthenticated! render :new diff --git a/lib/devise/controllers/filters.rb b/lib/devise/controllers/filters.rb index 12d107f3..1efc6535 100644 --- a/lib/devise/controllers/filters.rb +++ b/lib/devise/controllers/filters.rb @@ -10,31 +10,26 @@ module Devise end # The main accessor for the warden proxy instance - # def warden request.env['warden'] end # Sign in a user through warden - # def sign_in(scope) warden.authenticate(:scope => scope) end # Check if a user is authenticated or not performing the proper action. - # def sign_in!(scope) warden.authenticate!(:scope => scope) end # Proxy to the authenticated? method on warden - # def signed_in?(scope) warden.authenticated?(scope) end # Sign out based on scope - # def sign_out(scope, *args) warden.raw_session.inspect # Without this inspect here. The session does not clear. warden.logout(scope, *args) @@ -58,7 +53,6 @@ module Devise # Use: # before_filter :sign_in_user! # Tell devise to use :user map # before_filter :sign_in_admin! # Tell devise to use :admin map - # Devise.mappings.each_key do |mapping| class_eval <<-METHODS, __FILE__, __LINE__ def sign_in_#{mapping}! @@ -79,15 +73,31 @@ module Devise # # Example: # before_filter :require_no_authentication, :only => :new - # def require_no_authentication redirect_to root_path if warden.authenticated?(resource_name) end + # Checks whether it's a devise mapped resource or not. def is_devise_resource? raise ActionController::UnknownAction unless devise_mapping && devise_mapping.allows?(controller_name) end + # Redirects to stored uri before signing in or the default path and clear + # return to. + def redirect_back_or_to(default) + redirect_to(return_to || default) + clear_return_to + end + + # Access to scoped stored uri + def return_to + session[:"#{resource_name}.return_to"] + end + + # Clear scoped stored uri + def clear_return_to + session[:"#{resource_name}.return_to"] = nil + end end end end diff --git a/lib/devise/initializers/warden.rb b/lib/devise/initializers/warden.rb index fea999f6..ccce390b 100644 --- a/lib/devise/initializers/warden.rb +++ b/lib/devise/initializers/warden.rb @@ -1,6 +1,5 @@ # Taken from RailsWarden, thanks to Hassox. # http://github.com/hassox/rails_warden -# module Warden::Mixins::Common # Gets the rails request object by default if it's available def request @@ -25,19 +24,16 @@ end # Session Serialization in. This block determines how the user will be stored # in the session. If you're using a complex object like an ActiveRecord model, # it is not a good idea to store the complete object. An ID is sufficient. -# Warden::Manager.serialize_into_session{ |user| [user.class, user.id] } # Session Serialization out. This block gets the user out of the session. # It should be the reverse of serializing the object into the session -# Warden::Manager.serialize_from_session do |klass, id| klass.find(id) end # Default strategy for signing in a user, based on his email and password. -# If no email and no password are present, no authentication is tryed. -# +# If no email and no password are present, no authentication is attempted. Warden::Strategies.add(:authenticable) do def valid? @@ -48,33 +44,37 @@ Warden::Strategies.add(:authenticable) do # Authenticate a user based on email and password params, returning to warden # success and the authenticated user if everything is okay. Otherwise redirect - # to login page. - # + # to sign in page. def authenticate! - if valid_session? && resource = @mapping.to.authenticate(session) + if valid_attributes? && resource = @mapping.to.authenticate(attributes) success!(resource) else + store_location redirect!("/#{@mapping.as}/sign_in", :unauthenticated => true) end end - # Find the session for the current mapping. - # - def session - @session ||= request.params[scope] + # Find the attributes for the current mapping. + def attributes + @attributes ||= request.params[scope] end # Check for the right keys. - # - def valid_session? - session && session[:email].present? && session[:password].present? + def valid_attributes? + attributes && attributes[:email].present? && attributes[:password].present? end + # Stores requested uri to redirect the user after signing in. We cannot use + # scoped session provided by warden here, since the user is not authenticated + # yet, but we still need to store the uri based on scope, so different scopes + # would never use the same uri to redirect. + def store_location + session[:"#{@mapping.name}.return_to"] = request.request_uri if request.get? + end end # Adds Warden Manager to Rails middleware stack, configuring default devise # strategy and also the controller who will manage not authenticated users. -# Rails.configuration.middleware.use Warden::Manager do |manager| manager.default_strategies :authenticable manager.failure_app = SessionsController diff --git a/test/integration/authenticable_test.rb b/test/integration/authenticable_test.rb index d95023b9..a117e23f 100644 --- a/test/integration/authenticable_test.rb +++ b/test/integration/authenticable_test.rb @@ -155,8 +155,26 @@ class AuthenticationTest < ActionController::IntegrationTest end test 'render 404 on roles without permission' do - get "admin_area/password/new" + get 'admin_area/password/new' assert_response :not_found assert_not_contain 'Send me reset password instructions' end + + test 'return to default url if no one was requested' do + sign_in_as_user + + assert_template 'home/index' + assert_nil session[:return_to] + end + + test 'return to given url after sign in' do + get users_path + assert_redirected_to new_user_session_path(:unauthenticated => true) + assert_equal users_path, session[:"user.return_to"] + follow_redirect! + + sign_in_as_user :visit => false + assert_template 'users/index' + assert_nil session[:"user.return_to"] + end end diff --git a/test/integration/confirmable_test.rb b/test/integration/confirmable_test.rb index 01ec71c2..75737e1f 100644 --- a/test/integration/confirmable_test.rb +++ b/test/integration/confirmable_test.rb @@ -1,6 +1,6 @@ require 'test/test_helper' -class UsersConfirmationTest < ActionController::IntegrationTest +class ConfirmationTest < ActionController::IntegrationTest test 'user should be able to request a new confirmation' do user = create_user(:confirm => false) diff --git a/test/integration/recoverable_test.rb b/test/integration/recoverable_test.rb index 7442471a..d270d343 100644 --- a/test/integration/recoverable_test.rb +++ b/test/integration/recoverable_test.rb @@ -1,6 +1,6 @@ require 'test/test_helper' -class UsersPasswordRecoveryTest < ActionController::IntegrationTest +class PasswordTest < ActionController::IntegrationTest def visit_new_password_path visit new_user_session_path diff --git a/test/support/integration_tests_helper.rb b/test/support/integration_tests_helper.rb index b4016f36..33a1e4a7 100644 --- a/test/support/integration_tests_helper.rb +++ b/test/support/integration_tests_helper.rb @@ -25,7 +25,7 @@ class ActionController::IntegrationTest def sign_in_as_user(options={}, &block) create_user(options) - visit new_user_session_path + visit new_user_session_path unless options[:visit] == false fill_in 'email', :with => 'user@test.com' fill_in 'password', :with => '123456' yield if block_given? @@ -34,7 +34,7 @@ class ActionController::IntegrationTest def sign_in_as_admin(options={}, &block) create_admin(options) - visit new_admin_session_path + visit new_admin_session_path unless options[:visit] == false fill_in 'email', :with => 'admin@test.com' fill_in 'password', :with => '123456' yield if block_given?