This commit is contained in:
Thibaud Guillaume-Gentil 2011-02-15 11:17:37 +01:00
Родитель b399e73c96
Коммит 789edeb8de
8 изменённых файлов: 76 добавлений и 79 удалений

Просмотреть файл

@ -13,7 +13,7 @@ Cookies as secure and enables HSTS by default.
require 'rack/ssl-enforcer'
use Rack::SslEnforcer
Or, if you are using Bundler, just add this to your Gemfile:
gem 'rack-ssl-enforcer', :require => 'rack/ssl-enforcer'
@ -31,9 +31,9 @@ If all you want is SSL for your whole application, you are done! However, you ca
You might need the :redirect_to option if the requested URL can't be determined (e.g. if using a proxy).
config.middleware.use Rack::SslEnforcer, :redirect_to => 'https://example.org'
You can also define specific regex patterns or paths or hosts to redirect.
config.middleware.use Rack::SslEnforcer, :only => /^\/admin\//
config.middleware.use Rack::SslEnforcer, :only => "/login"
config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/]
@ -43,7 +43,7 @@ You can also define specific regex patterns or paths or hosts to redirect.
config.middleware.use Rack::SslEnforcer, :except_hosts => /[help|blog]\.example\.com$/
Note: hosts options take precedence over the path options. See tests for examples.
Use the :strict option to force http for all requests not matching your :only specification
config.middleware.use Rack::SslEnforcer, :only => ["/login", /\.xml$/], :strict => true
@ -75,13 +75,13 @@ Flagging cookies as secure functionality and HSTS support is greatly inspired by
== Note on Patches/Pull Requests
* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
future version unintentionally.
* Commit, do not mess with rakefile, version, or history.
(if you want to have your own version,
(if you want to have your own version,
that is fine but bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.

Просмотреть файл

@ -27,7 +27,7 @@ require 'rake/rdoctask'
require 'rack/ssl-enforcer/version'
Rake::RDocTask.new do |rdoc|
version = Rack::SslEnforcer::VERSION
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "rack-ssl-enforcer #{version}"
rdoc.rdoc_files.include('README*')

Просмотреть файл

@ -1 +1 @@
require 'rack/ssl-enforcer'
require 'rack/ssl-enforcer'

Просмотреть файл

@ -1,10 +1,10 @@
module Rack
class SslEnforcer
def initialize(app, options = {})
@app, @options = app, options
end
def call(env)
@req = Rack::Request.new(env)
if enforce_ssl?(@req)
@ -26,14 +26,13 @@ module Rack
@app.call(env)
end
end
private
def ssl_request?(env)
scheme(env) == 'https'
end
# Fixed in rack >= 1.3
def scheme(env)
if env['HTTPS'] == 'on'
@ -44,7 +43,7 @@ module Rack
env['rack.url_scheme']
end
end
def matches?(key, pattern, req)
if pattern.is_a?(Regexp)
case key
@ -103,7 +102,7 @@ module Rack
true
end
end
def replace_scheme(req, scheme)
Rack::Request.new(req.env.merge(
'rack.url_scheme' => scheme,
@ -112,7 +111,7 @@ module Rack
'SERVER_PORT' => port_for(scheme).to_s
))
end
def port_for(scheme)
scheme == 'https' ? 443 : 80
end
@ -129,7 +128,7 @@ module Rack
}.join("\n")
end
end
# see http://en.wikipedia.org/wiki/Strict_Transport_Security
def set_hsts_headers!(headers)
opts = { :expires => 31536000, :subdomains => true }.merge(@options[:hsts] || {})
@ -137,6 +136,6 @@ module Rack
value += "; includeSubDomains" if opts[:subdomains]
headers.merge!({ 'Strict-Transport-Security' => value })
end
end
end

Просмотреть файл

@ -2,4 +2,4 @@ module Rack
class SslEnforcer
VERSION = "0.2.0"
end
end
end

Просмотреть файл

@ -25,4 +25,4 @@ Gem::Specification.new do |s|
s.files = Dir.glob("{lib}/**/*") + %w[LICENSE README.rdoc]
s.require_path = 'lib'
end
end

Просмотреть файл

@ -10,9 +10,9 @@ require 'rack/ssl-enforcer'
class Test::Unit::TestCase
include Rack::Test::Methods
def app; Rack::Lint.new(@app); end
def mock_app(options = {})
main_app = lambda { |env|
request = Rack::Request.new(env)
@ -20,7 +20,7 @@ class Test::Unit::TestCase
headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly"
[200, headers, ['Hello world!']]
}
builder = Rack::Builder.new
builder.use Rack::SslEnforcer, options
builder.run main_app

Просмотреть файл

@ -1,22 +1,22 @@
require 'helper'
class TestRackSslEnforcer < Test::Unit::TestCase
context 'that has no :redirect_to set' do
setup { mock_app }
should 'respond with a ssl redirect to plain-text requests' do
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end
should 'respond with a ssl redirect to plain-text requests and keep params' do
get 'http://www.example.org/admin?token=33'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/admin?token=33', last_response.location
end
#heroku / etc do proxied SSL
#http://github.com/pivotal/refraction/issues/issue/2
should 'respect X-Forwarded-Proto header for proxied SSL' do
@ -24,13 +24,13 @@ class TestRackSslEnforcer < Test::Unit::TestCase
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end
should 'respond not redirect ssl requests' do
get 'https://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
should 'respond not redirect ssl requests and respect X-Forwarded-Proto header for proxied SSL' do
get 'http://www.example.org/', {}, { 'HTTP_X_FORWARDED_PROTO' => 'https', 'rack.url_scheme' => 'http' }
assert_equal 200, last_response.status
@ -41,114 +41,114 @@ class TestRackSslEnforcer < Test::Unit::TestCase
get 'http://example.org:81/', {}, { 'rack.url_scheme' => 'http' }
assert_equal 301, last_response.status
assert_equal 'https://example.org/', last_response.location
end
end
should 'secure cookies' do
get 'https://www.example.org/'
assert_equal ["id=1; path=/; secure", "token=abc; path=/; secure; HttpOnly"], last_response.headers['Set-Cookie'].split("\n")
end
should 'not set default hsts headers to all ssl requests' do
get 'https://www.example.org/'
assert last_response.headers["Strict-Transport-Security"].nil?
end
should 'not set hsts headers to non ssl requests' do
get 'http://www.example.org/'
assert last_response.headers["Strict-Transport-Security"].nil?
assert last_response.headers["Strict-Transport-Security"].nil?
end
end
context 'that has :redirect_to set' do
setup { mock_app :redirect_to => 'https://www.google.com' }
should 'respond with a ssl redirect to plain-text requests and redirect to :redirect_to' do
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.google.com', last_response.location
end
should 'respond not redirect ssl requests' do
get 'https://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end
context 'that has regex pattern as only option' do
setup { mock_app :only => /^\/admin/ }
should 'respond with a ssl redirect for /admin path' do
get 'http://www.example.org/admin'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/admin', last_response.location
end
should 'respond not redirect ssl requests' do
get 'http://www.example.org/foo'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
should 'secure cookies' do
get 'https://www.example.org/'
assert_equal ["id=1; path=/; secure", "token=abc; path=/; secure; HttpOnly"], last_response.headers['Set-Cookie'].split("\n")
end
end
context 'that has path as only option' do
setup { mock_app :only => "/login" }
should 'respond with a ssl redirect for /login path' do
get 'http://www.example.org/login'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/login', last_response.location
end
should 'respond not redirect ssl requests' do
get 'http://www.example.org/foo/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end
context 'that has array of regex pattern & path as only option' do
setup { mock_app :only => [/\.xml$/, "/login"] }
should 'respond with a ssl redirect for /login path' do
get 'http://www.example.org/login'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/login', last_response.location
end
should 'respond with a ssl redirect for /admin path' do
get 'http://www.example.org/users.xml'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/users.xml', last_response.location
end
should 'respond not redirect ssl requests' do
get 'http://www.example.org/foo/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end
context 'that has array of regex pattern & path as only option with strict option' do
setup { mock_app :only => [/\.xml$/, "/login"], :strict => 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/login'
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
@ -159,65 +159,65 @@ class TestRackSslEnforcer < Test::Unit::TestCase
get 'https://www.example.org/login'
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
end
context 'that has regex pattern as except option' do
setup { mock_app :except => /^\/foo/ }
should 'respond with a ssl redirect for /admin path' do
get 'http://www.example.org/admin'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/admin', last_response.location
end
should 'respond not redirect ssl requests' do
get 'http://www.example.org/foo'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
should 'secure cookies' do
get 'https://www.example.org/'
assert_equal ["id=1; path=/; secure", "token=abc; path=/; secure; HttpOnly"], last_response.headers['Set-Cookie'].split("\n")
end
end
context 'that has path as except option' do
setup { mock_app :except => "/foo" }
should 'respond with a ssl redirect for /login path' do
get 'http://www.example.org/login'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/login', last_response.location
end
should 'respond not redirect ssl requests' do
get 'http://www.example.org/foo/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end
context 'that has path as except option with strict option' do
setup { mock_app :except => "/foo", :strict => 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/login'
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/foo', {}, { 'rack.url_scheme' => 'https' }
assert_equal 301, last_response.status
@ -228,7 +228,7 @@ class TestRackSslEnforcer < Test::Unit::TestCase
get 'https://www.example.org/'
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/foo'
assert_equal ["id=1; path=/", "token=abc; path=/; secure; HttpOnly"], last_response.headers['Set-Cookie'].split("\n")
@ -463,19 +463,19 @@ class TestRackSslEnforcer < Test::Unit::TestCase
context 'that has array of regex pattern & domain as only_hosts option with strict option' do
setup { mock_app :only_hosts => [/[www|api]\.example\.org$/, "example.com"], :strict => true }
should 'respond with a http redirect from non-allowed https url' do
get 'https://abc.example.org/'
assert_equal 301, last_response.status
assert_equal 'http://abc.example.org/', last_response.location
end
should 'respond from allowed https url' do
get 'https://www.example.org/'
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://goo.example.org:80/', {}, { 'rack.url_scheme' => 'https' }
assert_equal 301, last_response.status
@ -486,7 +486,7 @@ class TestRackSslEnforcer < Test::Unit::TestCase
get 'https://www.example.org/'
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://goo.example.org/'
assert_equal ["id=1; path=/", "token=abc; path=/; secure; HttpOnly"], last_response.headers['Set-Cookie'].split("\n")
@ -533,19 +533,19 @@ class TestRackSslEnforcer < Test::Unit::TestCase
context 'that has regex pattern as except_hosts option with strict option' do
setup { mock_app :except_hosts => /[www|api]\.example\.org$/, :strict => true }
should 'respond with a http redirect from non-allowed https url' do
get 'https://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'http://www.example.org/', last_response.location
end
should 'respond from allowed https url' do
get 'https://abc.example.org/'
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://www.example.org:80/', {}, { 'rack.url_scheme' => 'https' }
assert_equal 301, last_response.status
@ -556,27 +556,25 @@ class TestRackSslEnforcer < Test::Unit::TestCase
get 'https://goo.example.org/'
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
end
context 'that has hsts options set' do
setup { mock_app :hsts => {:expires => '500', :subdomains => false} }
should 'set expiry option' do
get 'https://www.example.org/'
assert_equal "max-age=500", last_response.headers["Strict-Transport-Security"]
assert_equal "max-age=500", last_response.headers["Strict-Transport-Security"]
end
should 'not include subdomains' do
get 'https://www.example.org/'
assert !last_response.headers["Strict-Transport-Security"].include?("includeSubDomains")
end
end
end