diff --git a/lib/devise.rb b/lib/devise.rb index 12c6dfee..0bbefc02 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -63,6 +63,12 @@ module Devise end end -ActiveRecord::Base.send :extend, Devise::ActiveRecord -ActionController::Base.send :include, Devise::ActionController -ActionView::Base.send :include, Devise::ActionView +# Ensure to include Devise modules only after Rails initialization. +# This way application should have already defined Devise mappings and we are +# able to create default filters. +# +Rails.configuration.after_initialize do + ActiveRecord::Base.send :extend, Devise::ActiveRecord + ActionController::Base.send :include, Devise::ActionController + ActionView::Base.send :include, Devise::ActionView +end diff --git a/lib/devise/action_controller.rb b/lib/devise/action_controller.rb index 8bd36cc2..a9ec7263 100644 --- a/lib/devise/action_controller.rb +++ b/lib/devise/action_controller.rb @@ -6,6 +6,7 @@ module Devise include Devise::Controllers::Authenticable include Devise::Controllers::Resources include Devise::Controllers::UrlHelpers + include Devise::Controllers::Filters end end end diff --git a/lib/devise/controllers/authenticable.rb b/lib/devise/controllers/authenticable.rb index b895b410..5ef42402 100644 --- a/lib/devise/controllers/authenticable.rb +++ b/lib/devise/controllers/authenticable.rb @@ -25,7 +25,7 @@ module Devise # Proxy to the authenticated? method on warden # def authenticated?(scope=resource_name) - warden.authenticated?(scope) + warden.authenticated?(scope.to_sym) end alias_method :logged_in?, :authenticated? @@ -47,20 +47,6 @@ module Devise warden.raw_session.inspect # Without this inspect here. The session does not clear :| warden.logout(resource_name) end - - # Verify authenticated user and redirect to sign in if no authentication is found - # - def authenticate! - redirect_to new_session_path unless authenticated? - end - - # Helper for use in before_filters where no authentication is required: - # Example: - # before_filter :require_no_authentication, :only => :new - # - def require_no_authentication - redirect_to root_path if authenticated? - end end end end diff --git a/lib/devise/controllers/filters.rb b/lib/devise/controllers/filters.rb new file mode 100644 index 00000000..e6b19c8a --- /dev/null +++ b/lib/devise/controllers/filters.rb @@ -0,0 +1,45 @@ +module Devise + module Controllers + module Filters + + # Define authentication filters based on mappings. These filters should be + # used inside the controllers as before_filters, so you can control the + # scope of the user who should be signed in to access that specific + # controller/action. + # + # Example: + # Maps: + # Devise.map :users, :for => [:authenticable] + # Devise.map :admin, :for => [:authenticable] + # Generated Filters: + # user_authenticate! + # admin_authenticate! + # Use: + # before_filter :user_authenticate! # Tell devise to use :user map + # before_filter :admin_authenticate! # Tell devise to use :admin map + # + Devise.mappings.each_key do |mapping| + define_method(:"#{mapping}_authenticate!") do + authenticate!(mapping) + end + end + + # Verify authenticated user and redirect to sign in if no authentication + # is found + # + def authenticate!(scope) + redirect_to new_session_path(scope) unless authenticated?(scope) + end + + # Helper for use in before_filters where no authentication is required: + # Example: + # before_filter :require_no_authentication, :only => :new + # + def require_no_authentication + Devise.mappings.each_key do |map| + redirect_to root_path if authenticated?(map) + end + end + end + end +end diff --git a/lib/devise/controllers/resources.rb b/lib/devise/controllers/resources.rb index 7f82965f..a3f9bfb4 100644 --- a/lib/devise/controllers/resources.rb +++ b/lib/devise/controllers/resources.rb @@ -27,7 +27,7 @@ module Devise private def resource_name_or_request_path(object=nil) - object ? object.class.name : request.path + object ? (object.is_a?(::ActiveRecord::Base) ? object.class.name : object) : request.path end end end diff --git a/lib/devise/controllers/url_helpers.rb b/lib/devise/controllers/url_helpers.rb index 3b4643e8..396afcf9 100644 --- a/lib/devise/controllers/url_helpers.rb +++ b/lib/devise/controllers/url_helpers.rb @@ -22,7 +22,9 @@ module Devise actions.each do |action| class_eval <<-URL_HELPERS def #{action}#{module_name}_#{path_or_url}(*args) - resource = args.first.is_a?(::ActiveRecord::Base) ? args.shift : nil + resource = case args.first + when ::ActiveRecord::Base, Symbol, String then args.shift + end send("#{action}\#{resource_name(resource)}_#{module_name}_#{path_or_url}", *args) end URL_HELPERS diff --git a/test/controllers/filters_test.rb b/test/controllers/filters_test.rb new file mode 100644 index 00000000..188a38cf --- /dev/null +++ b/test/controllers/filters_test.rb @@ -0,0 +1,111 @@ +require 'test/test_helper' + +class FiltersController < ApplicationController + before_filter :user_authenticate!, :only => :user_action + before_filter :admin_authenticate!, :only => :admin_action + before_filter :require_no_authentication, :only => :not_authenticated_action + + def public_action + render :text => 'public' + end + + def not_authenticated_action + render :text => 'not_authenticated' + end + + def user_action + render :text => 'user' + end + + def admin_action + render :text => 'admin' + end +end + +class FiltersTest < ActionController::TestCase + tests FiltersController + + test 'generate user_authenticate! filter' do + assert @controller.respond_to?(:user_authenticate!) + end + + test 'proxy user_authenticate! to authenticate with user scope' do + @controller.expects(:authenticate!).with('user') + @controller.user_authenticate! + end + + test 'generate admin_authenticate! filter' do + assert @controller.respond_to?(:admin_authenticate!) + end + + test 'proxy admin_authenticate! to authenticate with user scope' do + @controller.expects(:authenticate!).with('admin') + @controller.admin_authenticate! + end + + test 'not authenticated user should be able to access public action' do + get :public_action + + assert_response :success + assert_equal 'public', @response.body + end + + test 'not authenticated as user should not be able to access user action' do + @controller.expects(:authenticated?).with('user').returns(false) + + get :user_action + assert_response :redirect + assert_redirected_to new_user_session_path + end + + test 'authenticated as user should be able to access user action' do + @controller.expects(:authenticated?).with('user').returns(true) + + get :user_action + assert_response :success + assert_equal 'user', @response.body + end + + test 'not authenticated as admin should not be able to access admin action' do + @controller.expects(:authenticated?).with('admin').returns(false) + + get :admin_action + assert_response :redirect + assert_redirected_to new_admin_session_path + end + + test 'authenticated as admin should be able to access admin action' do + @controller.expects(:authenticated?).with('admin').returns(true) + + get :admin_action + assert_response :success + assert_equal 'admin', @response.body + end + + test 'authenticated as user should not be able to access not authenticated action' do + @controller.expects(:authenticated?).with('user').returns(true) + @controller.expects(:authenticated?).with('admin').returns(false) + + get :not_authenticated_action + assert_response :redirect + assert_redirected_to root_path + end + + test 'authenticated as admin should not be able to access not authenticated action' do + @controller.expects(:authenticated?).with('user').returns(false) + @controller.expects(:authenticated?).with('admin').returns(true) + + get :not_authenticated_action + assert_response :redirect + assert_redirected_to root_path + end + + test 'not authenticated should access not_authenticated_action' do + @controller.expects(:authenticated?).with('user').returns(false) + @controller.expects(:authenticated?).with('admin').returns(false) + + get :not_authenticated_action + assert_response :success + assert_equal 'not_authenticated', @response.body + end +end diff --git a/test/controllers/url_helpers_test.rb b/test/controllers/url_helpers_test.rb index 623557bb..200e2978 100644 --- a/test/controllers/url_helpers_test.rb +++ b/test/controllers/url_helpers_test.rb @@ -6,21 +6,31 @@ class RoutesTest < ActionController::TestCase def test_path_and_url(name, prepend_path=nil) @request.path = '/users/session' prepend_path = "#{prepend_path}_" if prepend_path + + # No params assert_equal @controller.send(:"#{prepend_path}#{name}_path"), send(:"#{prepend_path}user_#{name}_path") assert_equal @controller.send(:"#{prepend_path}#{name}_url"), send(:"#{prepend_path}user_#{name}_url") + # Default url params assert_equal @controller.send(:"#{prepend_path}#{name}_path", :param => 123), send(:"#{prepend_path}user_#{name}_path", :param => 123) assert_equal @controller.send(:"#{prepend_path}#{name}_url", :param => 123), send(:"#{prepend_path}user_#{name}_url", :param => 123) @request.path = nil + # With an AR object assert_equal @controller.send(:"#{prepend_path}#{name}_path", User.new), send(:"#{prepend_path}user_#{name}_path") assert_equal @controller.send(:"#{prepend_path}#{name}_url", User.new), send(:"#{prepend_path}user_#{name}_url") + + # Using a symbol + assert_equal @controller.send(:"#{prepend_path}#{name}_path", :user), + send(:"#{prepend_path}user_#{name}_path") + assert_equal @controller.send(:"#{prepend_path}#{name}_url", :user), + send(:"#{prepend_path}user_#{name}_url") end diff --git a/test/rails_app/config/routes.rb b/test/rails_app/config/routes.rb index 08e8fade..7fe75152 100644 --- a/test/rails_app/config/routes.rb +++ b/test/rails_app/config/routes.rb @@ -2,4 +2,7 @@ ActionController::Routing::Routes.draw do |map| map.resources :users, :only => :index map.resources :admins, :only => :index map.root :controller => :home + + map.connect ':controller/:action/:id' + map.connect ':controller/:action/:id.:format' end