Skip to content

Commit 20ced47

Browse files
authored
Merge pull request rails#43444 from sabulikia/support-custom-csrf-strategy
Add support for custom CSRF strategies.
2 parents ea98781 + f40405c commit 20ced47

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

actionpack/lib/action_controller/metal/request_forgery_protection.rb

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,26 @@ module ClassMethods
124124
# If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
125125
# * <tt>:with</tt> - Set the method to handle unverified request.
126126
#
127-
# Valid unverified request handling methods are:
127+
# Built-in unverified request handling methods are:
128128
# * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
129129
# * <tt>:reset_session</tt> - Resets the session.
130130
# * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
131+
#
132+
# You can also implement custom strategy classes for unverified request handling:
133+
#
134+
# class CustomStrategy
135+
# def initialize(controller)
136+
# @controller = controller
137+
# end
138+
#
139+
# def handle_unverified_request
140+
# # Custom behaviour for unverfied request
141+
# end
142+
# end
143+
#
144+
# class ApplicationController < ActionController:x:Base
145+
# protect_from_forgery with: CustomStrategy
146+
# end
131147
def protect_from_forgery(options = {})
132148
options = options.reverse_merge(prepend: false)
133149

@@ -148,9 +164,18 @@ def skip_forgery_protection(options = {})
148164

149165
private
150166
def protection_method_class(name)
151-
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
152-
rescue NameError
153-
raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, or :reset_session"
167+
return name if name.is_a?(Class)
168+
169+
case name
170+
when :null_session
171+
ProtectionMethods::NullSession
172+
when :reset_session
173+
ProtectionMethods::ResetSession
174+
when :exception
175+
ProtectionMethods::Exception
176+
else
177+
raise ArgumentError, "Invalid request forgery protection method, use :null_session, :exception, :reset_session, or a custom forgery protection class."
178+
end
154179
end
155180
end
156181

actionpack/test/controller/request_forgery_protection_test.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,24 @@ def try_to_reset_session
103103
end
104104
end
105105

106+
class RequestForgeryProtectionControllerUsingCustomStrategy < ActionController::Base
107+
include RequestForgeryProtectionActions
108+
109+
class FakeException < Exception; end
110+
111+
class CustomStrategy
112+
def initialize(controller)
113+
@controller = controller
114+
end
115+
116+
def handle_unverified_request
117+
raise FakeException, "Raised a fake exception."
118+
end
119+
end
120+
121+
protect_from_forgery only: %w(index meta same_origin_js negotiate_same_origin), with: CustomStrategy
122+
end
123+
106124
class PrependProtectForgeryBaseController < ActionController::Base
107125
before_action :custom_action
108126
attr_accessor :called_callbacks
@@ -699,6 +717,7 @@ def setup
699717

700718
class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::TestCase
701719
include RequestForgeryProtectionTests
720+
702721
def assert_blocked(&block)
703722
assert_raises(ActionController::InvalidAuthenticityToken, &block)
704723
end
@@ -720,6 +739,14 @@ def test_raised_exception_message_explains_why_it_occurred
720739
end
721740
end
722741

742+
class RequestForgeryProtectionControllerUsingCustomStrategyTest < ActionController::TestCase
743+
include RequestForgeryProtectionTests
744+
745+
def assert_blocked(&block)
746+
assert_raises(RequestForgeryProtectionControllerUsingCustomStrategy::FakeException, &block)
747+
end
748+
end
749+
723750
class PrependProtectForgeryBaseControllerTest < ActionController::TestCase
724751
PrependTrueController = Class.new(PrependProtectForgeryBaseController) do
725752
protect_from_forgery prepend: true

0 commit comments

Comments
 (0)