require 'rspec_ext' require "#{__FILE__.dirname}/helper" require 'crystal/profiles/web_require' require 'crystal_ext/protect_from_forgery' require 'crystal/spec/environment' describe "Forgery protection" do with_environment :test before :all do class ForgerySpecHelper < Crystal::Processor def call block = workspace.check_forgery.before_request block.call workspace if block workspace.before_request_done = true next_processor.call block = workspace.check_forgery.after_request block.call workspace if block workspace.after_request_done = true end end class ::AnRemote inherit Crystal::HttpController protect_from_forgery :only => :protected_method def protected_method render :inline => 'protected result' end def method_without_protection render :inline => 'result' end def dumb_method; end end crystal.after :environment do crystal.conveyors.web do |web| web.use Crystal::Processors::PrepareParams web.use Crystal::Processors::EvaluateFormat web.use ForgerySpecHelper web.use Crystal::Processors::PrepareAutenticityToken web.use Crystal::Processors::ControllerCaller, :content end crystal.config.session = {'key' => 'session_id'} end end after :all do remove_constants %w(AnRemote ForgerySpecHelper) end def check_forgery opt workspace = nil result = Crystal::HTTPAdapter.mock_call({}, :check_forgery => opt.to_openobject) do |&block| block.call workspace = crystal[:workspace] end workspace.before_request_done.should be_true workspace.after_request_done.should be_true workspace end it "should set :authenticity_token only for :get and :html request" do check_forgery( :before_request => lambda{|workspace| workspace.env['REQUEST_METHOD'] = 'GET' workspace.env['CONTENT_TYPE'] = 'html' workspace.class = AnRemote workspace.method_name = 'dumb_method' }, :after_request => lambda{|workspace| workspace.request.session['authenticity_token'].should_not be_blank } ) # post check_forgery( :before_request => lambda{|workspace| workspace.env['REQUEST_METHOD'] = 'POST' workspace.env['CONTENT_TYPE'] = 'html' workspace.class = AnRemote workspace.method_name = 'dumb_method' }, :after_request => lambda{|workspace| workspace.request.session['authenticity_token'].should be_blank } ) end it "should check any non :get request with browser's formats for :authenticity_token" do lambda{ check_forgery( :before_request => lambda{|workspace| workspace.env['REQUEST_METHOD'] = 'POST' workspace.env['CONTENT_TYPE'] = 'html' workspace.class = AnRemote workspace.method_name = 'protected_method' } ) }.should raise_error(/Invalid authenticity token/) # with correct :authenticity_token check_forgery( :before_request => lambda{|workspace| workspace.env['REQUEST_METHOD'] = 'POST' workspace.env['CONTENT_TYPE'] = 'html' workspace.request.session['authenticity_token'] = 'secure token' workspace.params['authenticity_token'] = 'secure token' workspace.class = AnRemote workspace.method_name = 'protected_method' }, :after_request => lambda{|workspace| workspace.content.should == "protected result" } ) # non-browser format check_forgery( :before_request => lambda{|workspace| workspace.env['REQUEST_METHOD'] = 'POST' workspace.env['CONTENT_TYPE'] = 'non-browser-format' workspace.class = AnRemote workspace.method_name = 'protected_method' }, :after_request => lambda{|workspace| workspace.content.should == "protected result" } ) end it "should not protect non protected methods" do check_forgery( :before_request => lambda{|workspace| workspace.env['REQUEST_METHOD'] = 'POST' workspace.env['CONTENT_TYPE'] = 'html' workspace.class = AnRemote workspace.method_name = 'method_without_protection' }, :after_request => lambda{|workspace| workspace.content.should == "result" } ) end it "should use :session_authenticity_token from params (for flash support)" do check_forgery( :before_request => lambda{|workspace| workspace.env['REQUEST_METHOD'] = 'POST' workspace.params.format = 'html' workspace.params['session_authenticity_token'] = 'secure token' workspace.params['authenticity_token'] = 'secure token' workspace.class = AnRemote workspace.method_name = 'protected_method' }, :after_request => lambda{|workspace| workspace.content.should == "protected result" } ) end end