csrf protection (liquid tags + specs)

did committed Feb 02, 2015
commit c6375256cd758c820e185eba553a5dc60bdbd54c
Showing 9 changed files with 104 additions and 22 deletions
Gemfile.lock +3 -0
@@ @@ -17,6 +17,7 @@ PATH
moneta (~> 0.8.0)
morphine (~> 0.1.1)
rack-cache (~> 1.2)
+ rack_csrf (~> 2.5.0)
sprockets (~> 2.12.3)
sprockets-sass (~> 1.3.1)
@@ @@ -128,6 +129,8 @@ GEM
rack (>= 0.4)
rack-test (0.6.3)
rack (>= 1.0)
+ rack_csrf (2.5.0)
+ rack (>= 1.1.0)
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
rails-dom-testing (1.0.5)
locomotive/steam.rb b/lib/locomotive/steam.rb +1 -0
@@ @@ -18,6 +18,7 @@ require 'haml'
require 'compass'
require 'mimetype_fu'
require 'mime-types'
+ require 'rack/csrf'
require 'active_support'
require 'active_support/concern'
locomotive/steam/configuration.rb b/lib/locomotive/steam/configuration.rb +4 -0
@@ @@ -10,11 +10,15 @@ module Locomotive
attr_accessor :assets_path
attr_accessor :image_resizer_secret
+ attr_accessor :csrf_protection
+
def initialize
self.mode = :production
self.theme_assets_checksum = false
self.image_resizer_secret = 'please change it'
+
+ self.csrf_protection = true
end
end
locomotive/steam/liquid/tags/csrf.rb b/lib/locomotive/steam/liquid/tags/csrf.rb +16 -12
@@ @@ -7,11 +7,13 @@ module Locomotive
class Param < ::Liquid::Tag
def render(context)
- controller = context.registers[:controller]
- name = controller.send(:request_forgery_protection_token).to_s
- value = controller.send(:form_authenticity_token)
+ service = context.registers[:services].csrf_protection
- %(<input type="hidden" name="#{name}" value="#{value}">)
+ if service.enabled?
+ %(<input type="hidden" name="#{service.field}" value="#{service.token}" />)
+ else
+ ''
+ end
end
end
@@ @@ -19,14 +21,16 @@ module Locomotive
class Meta < ::Liquid::Tag
def render(context)
- controller = context.registers[:controller]
- name = controller.send(:request_forgery_protection_token).to_s
- value = controller.send(:form_authenticity_token)
-
- %{
- <meta name="csrf-param" content="#{name}">
- <meta name="csrf-token" content="#{value}">
- }
+ service = context.registers[:services].csrf_protection
+
+ if service.enabled?
+ %{
+ <meta name="csrf-param" content="#{service.field}" />
+ <meta name="csrf-token" content="#{service.token}" />
+ }
+ else
+ ''
+ end
end
end
locomotive/steam/middlewares/stack.rb b/lib/locomotive/steam/middlewares/stack.rb +6 -1
@@ @@ -25,10 +25,15 @@ module Locomotive
use Middlewares::StaticAssets, {
urls: ['/images', '/fonts', '/samples', '/media']
}
-
use Middlewares::DynamicAssets
end
+ use Rack::Csrf,
+ field: 'authenticity_token',
+ skip_if: -> (request) {
+ !(request.post? && request.params[:content_type_slug].present?)
+ }
+
use ::Dragonfly::Middleware, :steam
use Rack::Session::Moneta, options[:moneta]
locomotive/steam/services.rb b/lib/locomotive/steam/services.rb +4 -0
@@ @@ -42,6 +42,10 @@ module Locomotive
Services::ExternalAPI.new
end
+ register :csrf_protection do
+ Services::CsrfProtection.new(configuration.csrf_protection, Rack::Csrf.field, Rack::Csrf.token(request.env))
+ end
+
register :cache do
Services::NoCache.new
end
locomotive/steam/services/csrf_protection.rb b/lib/locomotive/steam/services/csrf_protection.rb +15 -0
@@ @@ -0,0 +1,15 @@
+ module Locomotive
+ module Steam
+ module Services
+
+ class CsrfProtection < Struct.new(:enabled, :field, :token)
+
+ def enabled?
+ !!enabled
+ end
+
+ end
+
+ end
+ end
+ end
locomotivecms_steam.gemspec +9 -9
@@ @@ -18,31 +18,31 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'bundler', '~> 1.7'
spec.add_development_dependency 'rake', '~> 10.4.2'
- # spec.add_development_dependency 'rspec', '~> 3.1.0'
- # spec.add_development_dependency 'vcr', '~> 2.9.3'
- # spec.add_development_dependency 'webmock', '~> 1.20.4'
- # spec.add_development_dependency 'mocha', '~> 1.20.4'
- # spec.add_development_dependency 'rack-test', '~> 0.6.3'
- # spec.add_development_dependency 'i18n-spec', '~> 0.6.0'
spec.add_dependency 'activesupport', '~> 4.2.0'
spec.add_dependency 'morphine', '~> 0.1.1'
spec.add_dependency 'httparty', '~> 0.13.3'
+
# spec.add_dependency 'httmultiparty', '~> 0.3.10'
spec.add_dependency 'rack-cache', '~> 1.2'
+ spec.add_dependency 'dragonfly', '~> 1.0.7'
spec.add_dependency 'moneta', '~> 0.8.0'
+ spec.add_dependency 'rack_csrf', '~> 2.5.0'
+
spec.add_dependency 'sprockets', '~> 2.12.3'
spec.add_dependency 'sprockets-sass', '~> 1.3.1'
- spec.add_dependency 'dragonfly', '~> 1.0.7'
+ spec.add_dependency 'coffee-script', '~> 2.3.0'
+ spec.add_dependency 'compass', '~> 1.0.3'
+
spec.add_dependency 'kaminari', '~> 0.16.2'
spec.add_dependency 'kramdown', '~> 1.5.0'
spec.add_dependency 'RedCloth', '~> 4.2.9'
- spec.add_dependency 'coffee-script', '~> 2.3.0'
spec.add_dependency 'haml', '~> 4.0.6'
- spec.add_dependency 'compass', '~> 1.0.3'
spec.add_dependency 'mimetype-fu', '~> 0.1.2'
+
+
# spec.add_dependency 'locomotivecms_models', '~> 0.0.1.pre.alpha'
spec.add_dependency 'locomotivecms-solid', '~> 4.0.0.alpha'
spec.add_dependency 'locomotivecms_common', '~> 0.0.2'
spec/unit/liquid/tags/csrf_spec.rb +46 -0
@@ @@ -0,0 +1,46 @@
+ require 'spec_helper'
+
+ describe Locomotive::Steam::Liquid::Tags::Csrf do
+
+ before do
+ allow(Rack::Csrf).to receive(:field).and_return('token')
+ allow(Rack::Csrf).to receive(:token).and_return(42)
+ end
+
+ let(:request) { instance_double('Request', env: {}) }
+ let(:services) { Locomotive::Steam::Services.build_instance(request) }
+ let(:context) { ::Liquid::Context.new({}, {}, { services: services }) }
+
+ subject { render_template(template, context) }
+
+ describe 'csrf_param' do
+
+ let(:template) { '{% csrf_param %}' }
+ it { is_expected.to eq '<input type="hidden" name="token" value="42" />' }
+
+ context 'protection not enabled' do
+
+ before { allow(services.configuration).to receive(:csrf_protection).and_return(false) }
+ it { is_expected.to eq '' }
+
+ end
+
+ end
+
+ describe 'rendering the meta tag used by ajax requests' do
+
+ let(:template) { '{% csrf_meta %}' }
+
+ it { is_expected.to match '<meta name="csrf-param" content="token" />' }
+ it { is_expected.to match '<meta name="csrf-token" content="42" />' }
+
+ context 'protection not enabled' do
+
+ before { allow(services.configuration).to receive(:csrf_protection).and_return(false) }
+ it { is_expected.to eq '' }
+
+ end
+
+ end
+
+ end