rack-ssl-facebook 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
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: []