Skip to content

Commit

Permalink
Add option for :mixed enforcement
Browse files Browse the repository at this point in the history
There are cases where it is necessary to allow ssl on put or post but not get.
With restful routes this is harder since the actual route matches but the method changes.
The mixed option takes this into account and only forces strict on get and delete.

So the following:
  config.middleware.use Rack::SslEnforcer, :only => [/^\/posts\/(.+)\/edit/], :mixed => true

Above will allow PUT#post/:id to maintain the secure url while GET#post/:id will be forced to use http.
  • Loading branch information
lardawge committed Feb 3, 2011
1 parent 0d27c9d commit cb2959a
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
6 changes: 6 additions & 0 deletions README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ And force http for non-https path

config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/], :strict => true

Or in the case where you have matching urls with different methods (rails restful routes: get#users post#users || get#user/:id put#user/:id) you may need to post and put to secure but redirect to http on get.

config.middleware.use Rack::SslEnforcer, :only => [/^\/users\/(.+)\/edit/], :mixed => true

The above will allow you to post/put from the secure/non-secure urls keeping the original schema.

To set HSTS expiry and subdomain inclusion (defaults: one year, true)

config.middleware.use Rack::SslEnforcer, :hsts => {:expires => 500, :subdomains => false}
Expand Down
12 changes: 9 additions & 3 deletions lib/rack/ssl-enforcer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def call(env)
@req = Rack::Request.new(env)
if enforce_ssl?(env)
scheme = 'https' unless ssl_request?(env)
elsif ssl_request?(env) && @options[:strict]
elsif ssl_request?(env) && enforcement_non_ssl?(env)
scheme = 'http'
end

Expand All @@ -26,9 +26,15 @@ def call(env)
@app.call(env)
end
end



private

def enforcement_non_ssl?(env)
return true if @options[:strict]
unless (env['REQUEST_METHOD'] == 'PUT' || env['REQUEST_METHOD'] == 'POST')
@options[:mixed] if @options[:mixed]
end
end

def ssl_request?(env)
scheme(env) == 'https'
Expand Down
56 changes: 56 additions & 0 deletions test/rack-ssl-enforcer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,62 @@ class TestRackSslEnforcer < Test::Unit::TestCase
end
end

context 'that has array of regex pattern & path as only option with strict option and post option' do
setup { mock_app :only => [/^\/users\/(.+)\/edit/], :mixed => true }

should 'respond with a http redirect from non-allowed https url' do
get 'https://www.example.org/foo/'
assert_equal 301, last_response.status
assert_equal 'http://www.example.org/foo/', last_response.location
end

should 'respond from allowed https url' do
get 'https://www.example.org/users/123/edit'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end

should 'use default https port when redirecting non-standard ssl port to http' do
get 'https://example.org:81/', {}, { 'rack.url_scheme' => 'https' }
assert_equal 301, last_response.status
assert_equal 'http://example.org/', last_response.location
end

should 'secure cookies' do
get 'https://www.example.org/users/123/edit'
assert_equal ["id=1; path=/; secure", "token=abc; path=/; secure; HttpOnly"], last_response.headers['Set-Cookie'].split("\n")
end

should 'not secure cookies' do
get 'http://www.example.org/'
assert_equal ["id=1; path=/", "token=abc; path=/; secure; HttpOnly"], last_response.headers['Set-Cookie'].split("\n")
end

should 'not redirect if post' do
post 'https://www.example.org/users/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end

should 'not redirect if put' do
put 'https://www.example.org/users/123'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end

should 'not redirect if post' do
post 'http://www.example.org/users/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end

should 'not redirect if put' do
put 'http://www.example.org/users/123'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end

context 'that has hsts options set' do
setup { mock_app :hsts => {:expires => '500', :subdomains => false} }

Expand Down

0 comments on commit cb2959a

Please sign in to comment.