rack-ssl-facebook 1.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :gemcutter
2
+
3
+ gem 'rack'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Joshua Peek
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ Rack::SSL
2
+ =========
3
+
4
+ Force SSL/TLS in your app.
5
+
6
+ 1. Redirects all "http" requests to "https"
7
+ 2. Set `Strict-Transport-Security` header
8
+ 3. Flag all cookies as "secure"
9
+
10
+ Usage
11
+ -----
12
+
13
+ use Rack::SSL
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rack-ssl-facebook"
8
+ gem.summary = "Force SSL/TLS in your app, preserving facebook's signed_request."
9
+ gem.description = "Rack middleware to force SSL/TLS, preserving facebook's signed_request."
10
+ gem.email = "mail@recursive-design.com"
11
+ gem.homepage = "https://github.com/recurser/rack-ssl-facebook"
12
+ gem.authors = ["Dave Perrett"]
13
+ gem.rubyforge_project = 'nowarning'
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ task :default => :test
22
+ Rake::TestTask.new do |t|
23
+ t.warning = true
24
+ end
25
+
26
+ def version
27
+ File.exist?('VERSION') ? File.read('VERSION') : ""
28
+ end
29
+
30
+ namespace :gem do
31
+
32
+ desc 'Clean build products'
33
+ task :clean do
34
+ rm_f 'pkg'
35
+ end
36
+
37
+ desc 'Build the gem'
38
+ task :build => :clean do
39
+ system 'rake gemspec'
40
+ system 'rake build'
41
+ end
42
+
43
+ desc 'Push the gem to rubygems'
44
+ task :publish => [:clean, :build] do
45
+ system "gem push pkg/rack-ssl-facebook-#{version}.gem"
46
+ end
47
+
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.3.4
@@ -0,0 +1,100 @@
1
+ require 'rack'
2
+ require 'rack/request'
3
+
4
+ module Rack
5
+ class SSLFacebook
6
+ YEAR = 31536000
7
+
8
+ def self.default_hsts_options
9
+ { :expires => YEAR, :subdomains => false }
10
+ end
11
+
12
+ def initialize(app, options = {})
13
+ @app = app
14
+
15
+ @hsts = options[:hsts]
16
+ @hsts = {} if @hsts.nil? || @hsts == true
17
+ @hsts = self.class.default_hsts_options.merge(@hsts) if @hsts
18
+
19
+ @exclude = options[:exclude]
20
+ @host = options[:host]
21
+ @port = options[:port]
22
+ end
23
+
24
+ def call(env)
25
+ if @exclude && @exclude.call(env)
26
+ @app.call(env)
27
+ elsif scheme(env) == 'https'
28
+ status, headers, body = @app.call(env)
29
+ headers = hsts_headers.merge(headers)
30
+ flag_cookies_as_secure!(headers)
31
+ [status, headers, body]
32
+ else
33
+ redirect_to_https(env)
34
+ end
35
+ end
36
+
37
+ private
38
+ # Fixed in rack >= 1.3
39
+ def scheme(env)
40
+ if env['HTTPS'] == 'on'
41
+ 'https'
42
+ elsif env['HTTP_X_FORWARDED_PROTO']
43
+ env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
44
+ else
45
+ env['rack.url_scheme']
46
+ end
47
+ end
48
+
49
+ def redirect_to_https(env)
50
+ req = Request.new(env)
51
+ url = URI(req.url)
52
+ url.scheme = "https"
53
+ url.host = @host if @host
54
+ url.port = @port if @port
55
+
56
+ # If a signed_request is present, append it to the query string.
57
+ if req.POST['signed_request']
58
+ if url.query.nil? or url.query.empty?
59
+ url.query = "signed_request=#{req.POST['signed_request']}"
60
+ else
61
+ url.query += "&signed_request=#{req.POST['signed_request']}"
62
+ end
63
+ end
64
+
65
+ headers = hsts_headers.merge('Content-Type' => 'text/html',
66
+ 'Location' => url.to_s)
67
+
68
+ [301, headers, []]
69
+ end
70
+
71
+ # http://tools.ietf.org/html/draft-hodges-strict-transport-sec-02
72
+ def hsts_headers
73
+ if @hsts
74
+ value = "max-age=#{@hsts[:expires]}"
75
+ value += "; includeSubDomains" if @hsts[:subdomains]
76
+ { 'Strict-Transport-Security' => value }
77
+ else
78
+ {}
79
+ end
80
+ end
81
+
82
+ def flag_cookies_as_secure!(headers)
83
+ if cookies = headers['Set-Cookie']
84
+ # Rack 1.1's set_cookie_header! will sometimes wrap
85
+ # Set-Cookie in an array
86
+ unless cookies.respond_to?(:to_ary)
87
+ cookies = cookies.split("\n")
88
+ end
89
+
90
+ headers['Set-Cookie'] = cookies.map { |cookie|
91
+ if cookie !~ /; secure(;|$)/
92
+ "#{cookie}; secure"
93
+ else
94
+ cookie
95
+ end
96
+ }.join("\n")
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,47 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "rack-ssl-facebook"
8
+ s.version = "1.3.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Dave Perrett"]
12
+ s.date = "2011-12-05"
13
+ s.description = "Rack middleware to force SSL/TLS, preserving facebook's signed_request."
14
+ s.email = "mail@recursive-design.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "LICENSE",
22
+ "README.md",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/rack/ssl_facebook.rb",
26
+ "rack-ssl-facebook.gemspec",
27
+ "test/test_ssl.rb"
28
+ ]
29
+ s.homepage = "https://github.com/recurser/rack-ssl-facebook"
30
+ s.require_paths = ["lib"]
31
+ s.rubyforge_project = "nowarning"
32
+ s.rubygems_version = "1.8.11"
33
+ s.summary = "Force SSL/TLS in your app, preserving facebook's signed_request."
34
+
35
+ if s.respond_to? :specification_version then
36
+ s.specification_version = 3
37
+
38
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
39
+ s.add_runtime_dependency(%q<rack>, [">= 0"])
40
+ else
41
+ s.add_dependency(%q<rack>, [">= 0"])
42
+ end
43
+ else
44
+ s.add_dependency(%q<rack>, [">= 0"])
45
+ end
46
+ end
47
+
data/test/test_ssl.rb ADDED
@@ -0,0 +1,169 @@
1
+ require 'rack/ssl_facebook'
2
+
3
+ require 'test/unit'
4
+ require 'rack/test'
5
+
6
+ class TestSSL < Test::Unit::TestCase
7
+ include Rack::Test::Methods
8
+
9
+ def default_app
10
+ lambda { |env|
11
+ headers = {'Content-Type' => "text/html"}
12
+ headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly"
13
+ [200, headers, ["OK"]]
14
+ }
15
+ end
16
+
17
+ def app
18
+ @app ||= Rack::SSLFacebook.new(default_app)
19
+ end
20
+ attr_writer :app
21
+
22
+ def test_allows_https_url
23
+ get "https://example.org/path?key=value"
24
+ assert last_response.ok?
25
+ end
26
+
27
+ def test_allows_https_proxy_header_url
28
+ get "http://example.org/", {}, 'HTTP_X_FORWARDED_PROTO' => "https"
29
+ assert last_response.ok?
30
+ end
31
+
32
+ def test_redirects_http_to_https
33
+ get "http://example.org/path?key=value"
34
+ assert last_response.redirect?
35
+ assert_equal "https://example.org/path?key=value",
36
+ last_response.headers['Location']
37
+ end
38
+
39
+ def test_exclude_from_redirect
40
+ self.app = Rack::SSLFacebook.new(default_app, :exclude => lambda { |env| true })
41
+ get "http://example.org/"
42
+ assert last_response.ok?
43
+ end
44
+
45
+ def test_hsts_header_by_default
46
+ get "https://example.org/"
47
+ assert_equal "max-age=31536000",
48
+ last_response.headers['Strict-Transport-Security']
49
+ end
50
+
51
+ def test_hsts_header
52
+ self.app = Rack::SSLFacebook.new(default_app, :hsts => true)
53
+ get "https://example.org/"
54
+ assert_equal "max-age=31536000",
55
+ last_response.headers['Strict-Transport-Security']
56
+ end
57
+
58
+ def test_disable_hsts_header
59
+ self.app = Rack::SSLFacebook.new(default_app, :hsts => false)
60
+ get "https://example.org/"
61
+ assert !last_response.headers['Strict-Transport-Security']
62
+ end
63
+
64
+ def test_hsts_expires
65
+ self.app = Rack::SSLFacebook.new(default_app, :hsts => { :expires => 500 })
66
+ get "https://example.org/"
67
+ assert_equal "max-age=500",
68
+ last_response.headers['Strict-Transport-Security']
69
+ end
70
+
71
+ def test_hsts_include_subdomains
72
+ self.app = Rack::SSLFacebook.new(default_app, :hsts => { :subdomains => true })
73
+ get "https://example.org/"
74
+ assert_equal "max-age=31536000; includeSubDomains",
75
+ last_response.headers['Strict-Transport-Security']
76
+ end
77
+
78
+ def test_flag_cookies_as_secure
79
+ get "https://example.org/"
80
+ assert_equal ["id=1; path=/; secure", "token=abc; path=/; secure; HttpOnly" ],
81
+ last_response.headers['Set-Cookie'].split("\n")
82
+ end
83
+
84
+ def test_flag_cookies_as_secure_at_end_of_line
85
+ self.app = Rack::SSLFacebook.new(lambda { |env|
86
+ headers = {
87
+ 'Content-Type' => "text/html",
88
+ 'Set-Cookie' => "problem=def; path=/; HttpOnly; secure"
89
+ }
90
+ [200, headers, ["OK"]]
91
+ })
92
+
93
+ get "https://example.org/"
94
+ assert_equal ["problem=def; path=/; HttpOnly; secure"],
95
+ last_response.headers['Set-Cookie'].split("\n")
96
+ end
97
+
98
+ def test_legacy_array_headers
99
+ self.app = Rack::SSLFacebook.new(lambda { |env|
100
+ headers = {
101
+ 'Content-Type' => "text/html",
102
+ 'Set-Cookie' => ["id=1; path=/", "token=abc; path=/; HttpOnly"]
103
+ }
104
+ [200, headers, ["OK"]]
105
+ })
106
+
107
+ get "https://example.org/"
108
+ assert_equal ["id=1; path=/; secure", "token=abc; path=/; HttpOnly; secure"],
109
+ last_response.headers['Set-Cookie'].split("\n")
110
+ end
111
+
112
+ def test_no_cookies
113
+ self.app = Rack::SSLFacebook.new(lambda { |env|
114
+ [200, {'Content-Type' => "text/html"}, ["OK"]]
115
+ })
116
+ get "https://example.org/"
117
+ assert !last_response.headers['Set-Cookie']
118
+ end
119
+
120
+ def test_redirect_to_host
121
+ self.app = Rack::SSLFacebook.new(default_app, :host => "ssl.example.org")
122
+ get "http://example.org/path?key=value"
123
+ assert_equal "https://ssl.example.org/path?key=value",
124
+ last_response.headers['Location']
125
+ end
126
+
127
+ def test_redirect_to_port
128
+ self.app = Rack::SSLFacebook.new(default_app, :port => 8443)
129
+ get "http://example.org/path?key=value"
130
+ assert_equal "https://example.org:8443/path?key=value",
131
+ last_response.headers['Location']
132
+ end
133
+
134
+ def test_redirect_to_host_and_port
135
+ self.app = Rack::SSLFacebook.new(default_app, :host => "ssl.example.org", :port => 8443)
136
+ get "http://example.org/path?key=value"
137
+ assert_equal "https://ssl.example.org:8443/path?key=value",
138
+ last_response.headers['Location']
139
+ end
140
+
141
+ def test_redirect_to_secure_host_when_on_subdomain
142
+ self.app = Rack::SSLFacebook.new(default_app, :host => "ssl.example.org")
143
+ get "http://ssl.example.org/path?key=value"
144
+ assert_equal "https://ssl.example.org/path?key=value",
145
+ last_response.headers['Location']
146
+ end
147
+
148
+ def test_redirect_to_secure_subdomain_when_on_deep_subdomain
149
+ self.app = Rack::SSLFacebook.new(default_app, :host => "example.co.uk")
150
+ get "http://double.rainbow.what.does.it.mean.example.co.uk/path?key=value"
151
+ assert_equal "https://example.co.uk/path?key=value",
152
+ last_response.headers['Location']
153
+ end
154
+
155
+ def test_redirect_with_signed_request
156
+ self.app = Rack::SSLFacebook.new(default_app, :host => "ssl.example.org")
157
+ post "http://example.org/path", {:signed_request => '1234'}
158
+ assert_equal "https://ssl.example.org/path?signed_request=1234",
159
+ last_response.headers['Location']
160
+ end
161
+
162
+ def test_redirect_with_signed_request_multi_params
163
+ self.app = Rack::SSLFacebook.new(default_app, :host => "ssl.example.org")
164
+ post "http://example.org/path?key=value", {:signed_request => '1234'}
165
+ assert_equal "https://ssl.example.org/path?key=value&signed_request=1234",
166
+ last_response.headers['Location']
167
+ end
168
+
169
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-ssl-facebook
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dave Perrett
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-05 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: &70095368008140 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70095368008140
25
+ description: Rack middleware to force SSL/TLS, preserving facebook's signed_request.
26
+ email: mail@recursive-design.com
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files:
30
+ - LICENSE
31
+ - README.md
32
+ files:
33
+ - Gemfile
34
+ - LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - VERSION
38
+ - lib/rack/ssl_facebook.rb
39
+ - rack-ssl-facebook.gemspec
40
+ - test/test_ssl.rb
41
+ homepage: https://github.com/recurser/rack-ssl-facebook
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project: nowarning
61
+ rubygems_version: 1.8.11
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Force SSL/TLS in your app, preserving facebook's signed_request.
65
+ test_files: []