diff --git a/padrino-core/lib/padrino-core/application.rb b/padrino-core/lib/padrino-core/application.rb index 637b6069d..f426aa5cc 100644 --- a/padrino-core/lib/padrino-core/application.rb +++ b/padrino-core/lib/padrino-core/application.rb @@ -339,11 +339,28 @@ def setup_csrf_protection(builder) check_csrf_protection_dependency if protect_from_csrf? + if protect_from_csrf.is_a?(Hash) + except = Array(protect_from_csrf[:except]) + if !except.empty? + except.each{|except_path| exception_router.before(except_path, &ignore_csrf_protection) } + builder.use exception_router + end + end builder.use(Rack::Protection::AuthenticityToken, options_for_csrf_protection_setup) end end + # the sinatra app for routes to disable the csrf protection. + def exception_router + @exception_router ||= Class.new(Sinatra::Base) + end + + # returns a proc to avoid csrf protection. + def ignore_csrf_protection + Proc.new { env['HTTP_X_CSRF_TOKEN'] = session[:csrf] = SecureRandom.hex(32) } + end + # returns the options used in the builder for csrf protection setup def options_for_csrf_protection_setup options = { :logger => logger } diff --git a/padrino-core/test/test_csrf_protection.rb b/padrino-core/test/test_csrf_protection.rb index fd6c7bd74..f87f2f5fe 100644 --- a/padrino-core/test/test_csrf_protection.rb +++ b/padrino-core/test/test_csrf_protection.rb @@ -76,5 +76,50 @@ assert_equal 403, status end end + + context "with :except option" do + before do + mock_app do + enable :sessions + set :protect_from_csrf, :except => ["/", "/foo"] + post("/") { "Hello" } + post("/foo") { "Hello, foo" } + post("/bar") { "Hello, bar" } + end + end + + should "allow ignoring CSRF protection on specific routes" do + post "/" + assert_equal 200, status + post "/foo" + assert_equal 200, status + post "/bar" + assert_equal 403, status + end + end + + context "with middleware" do + before do + class Middleware < Sinatra::Base + post("/middleware") { "Hello, middleware" } + post("/dummy") { "Hello, dummy" } + end + mock_app do + enable :sessions + set :protect_from_csrf, :except => ["/", "/middleware"] + use Middleware + post("/") { "Hello" } + end + end + + should "allow ignoring CSRF protection on specific routes of middleware" do + post "/" + assert_equal 200, status + post "/middleware" + assert_equal 200, status + post "/dummy" + assert_equal 403, status + end + end end -end \ No newline at end of file +end