diff --git a/lib/lotus/action/params.rb b/lib/lotus/action/params.rb
index 7d355b0f..c312bd08 100644
--- a/lib/lotus/action/params.rb
+++ b/lib/lotus/action/params.rb
@@ -26,6 +26,20 @@ class Params
# @since 0.1.0
ROUTER_PARAMS = 'router.params'.freeze
+ # CSRF params key
+ #
+ # This key is shared with lotusrb and lotus-helpers
+ #
+ # @since x.x.x
+ # @api private
+ CSRF_TOKEN = '_csrf_token'.freeze
+
+ # Set of params that are never filtered
+ #
+ # @since x.x.x
+ # @api private
+ DEFAULT_PARAMS = Hash[CSRF_TOKEN => true].freeze
+
# Separator for #get
#
# @since 0.4.0
@@ -201,6 +215,18 @@ def to_h
end
alias_method :to_hash, :to_h
+ # Assign CSRF Token.
+ # This method is here for compatibility with Lotus::Validations.
+ #
+ # NOTE: When we will not support indifferent access anymore, we can probably
+ # remove this method.
+ #
+ # @since x.x.x
+ # @api private
+ def _csrf_token=(value)
+ @attributes.set(CSRF_TOKEN, value)
+ end
+
private
# @since 0.3.1
# @api private
@@ -236,12 +262,20 @@ def _params
def _whitelisted_params
{}.tap do |result|
_raw.to_h.each do |k, v|
- next unless self.class.defined_attributes.include?(k.to_s)
+ next unless assign_attribute?(k)
result[k] = v
end
end
end
+
+ # Override Lotus::Validations method
+ #
+ # @since x.x.x
+ # @api private
+ def assign_attribute?(key)
+ DEFAULT_PARAMS[key.to_s] || super
+ end
end
end
end
diff --git a/lib/lotus/action/rack.rb b/lib/lotus/action/rack.rb
index b4d58017..06205745 100644
--- a/lib/lotus/action/rack.rb
+++ b/lib/lotus/action/rack.rb
@@ -255,7 +255,15 @@ def send_file(path)
#
# @since 0.3.2
def head?
- @_env[REQUEST_METHOD] == HEAD
+ request_method == HEAD
+ end
+
+ # NOTE: Lotus::Action::CSRFProtection (lotusrb gem) depends on this.
+ #
+ # @api private
+ # @since x.x.x
+ def request_method
+ @_env[REQUEST_METHOD]
end
end
end
diff --git a/lib/lotus/action/session.rb b/lib/lotus/action/session.rb
index 645dd7c7..b326bc49 100644
--- a/lib/lotus/action/session.rb
+++ b/lib/lotus/action/session.rb
@@ -20,6 +20,16 @@ module Session
# @api private
ERRORS_KEY = :__errors
+ # Add session to default exposures
+ #
+ # @since x.x.x
+ # @api private
+ def self.included(action)
+ action.class_eval do
+ expose :session
+ end
+ end
+
protected
# Gets the session from the request and expose it as an Hash.
diff --git a/test/action/params_test.rb b/test/action/params_test.rb
index 21c1155f..24123006 100644
--- a/test/action/params_test.rb
+++ b/test/action/params_test.rb
@@ -23,9 +23,11 @@
end
it 'raw gets all params' do
- @action.call({id: 1, unknown: 2})
- @action.params.raw.get(:id).must_equal 1
- @action.params.raw.get(:unknown).must_equal 2
+ @action.call({id: '1', unknown: '2', _csrf_token: '3'})
+
+ @action.params.raw.get(:id).must_equal '1'
+ @action.params.raw.get(:unknown).must_equal '2'
+ @action.params.raw.get(:_csrf_token).must_equal '3'
end
end
@@ -35,9 +37,11 @@
end
it 'raw gets all params' do
- @action.call({id: 1, unknown: 2})
- @action.params.raw.get(:id).must_equal 1
- @action.params.raw.get(:unknown).must_equal 2
+ @action.call({id: '1', unknown: '2', _csrf_token: '3'})
+
+ @action.params.raw.get(:id).must_equal '1'
+ @action.params.raw.get(:unknown).must_equal '2'
+ @action.params.raw.get(:_csrf_token).must_equal '3'
end
end
end
@@ -100,6 +104,11 @@
_, _, body = @action.call({id: 23, unknown: 4})
body.must_equal [%({"id"=>23})]
end
+
+ it "doesn't filter _csrf_token" do
+ _, _, body = @action.call(_csrf_token: 'abc')
+ body.must_equal [%({"_csrf_token"=>"abc"})]
+ end
end
describe "in a Rack context" do
@@ -107,6 +116,11 @@
response = Rack::MockRequest.new(@action).request('PATCH', "?id=23", params: { x: { foo: 'bar' } })
response.body.must_match %({"id"=>"23"})
end
+
+ it "doesn't filter _csrf_token" do
+ response = Rack::MockRequest.new(@action).request('PATCH', "?id=23", params: { _csrf_token: 'def', x: { foo: 'bar' } })
+ response.body.must_match %("_csrf_token"=>"def")
+ end
end
describe "with Lotus::Router" do
diff --git a/test/fixtures.rb b/test/fixtures.rb
index 3ef7b8b0..e748dcf4 100644
--- a/test/fixtures.rb
+++ b/test/fixtures.rb
@@ -1207,3 +1207,11 @@ def call(env)
end
end
end
+
+class MethodInspectionAction
+ include Lotus::Action
+
+ def call(params)
+ self.body = request_method
+ end
+end
diff --git a/test/rack_test.rb b/test/rack_test.rb
new file mode 100644
index 00000000..cebb23fc
--- /dev/null
+++ b/test/rack_test.rb
@@ -0,0 +1,16 @@
+require 'test_helper'
+
+describe Lotus::Action::Rack do
+ before do
+ @action = MethodInspectionAction.new
+ end
+
+ ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'TRACE', 'OPTIONS'].each do |verb|
+ it "returns current request method (#{ verb })" do
+ env = Rack::MockRequest.env_for('/', method: verb)
+ _, _, body = @action.call(env)
+
+ body.must_equal [verb]
+ end
+ end
+end
diff --git a/test/session_test.rb b/test/session_test.rb
index 702a829f..e3de543f 100644
--- a/test/session_test.rb
+++ b/test/session_test.rb
@@ -6,14 +6,21 @@
action = SessionAction.new
action.call({'rack.session' => session = { 'user_id' => '23' }})
- action.send(:session).must_equal(session)
+ action.__send__(:session).must_equal(session)
end
it 'returns empty hash when it is missing' do
action = SessionAction.new
action.call({})
- action.send(:session).must_equal({})
+ action.__send__(:session).must_equal({})
+ end
+
+ it 'exposes session' do
+ action = SessionAction.new
+ action.call({'rack.session' => session = { 'foo' => 'bar' }})
+
+ action.exposures[:session].must_equal(session)
end
end
end