tynn 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a6cd976ef2725374c76761726c9c86a4714a0aed
4
- data.tar.gz: f769dcbcabcc8ac1941a175e2280d860cdf3304e
3
+ metadata.gz: c01166fc9c243f8e396f2b5f0f9007681524c7f0
4
+ data.tar.gz: f09380715acf0839c39dba34ead60ff161ec4aaf
5
5
  SHA512:
6
- metadata.gz: 304636238779f980affe65c6028814bcbe63823264baf249e599ec8fbf1dd002dd70947c0a3cce5b4e801afea6a20210dfcf1c64d07bd65b0835285d382ce53c
7
- data.tar.gz: f48e2523ac152117c16c79972a8ada5ed7962fca259904fdd4ae1f3b562931ce0bf480c0684154b43379c0c739164578d3623697d14a3797760eb5807105702b
6
+ metadata.gz: 8e14f1f08ec671c79ed112f81b1420569498ea7e172a4e6faf6ecc2ed45aea7ac1cdc6c7307183fc71944c0c4ddf947ebf2e7b95d2f24544a0880efca47682e1
7
+ data.tar.gz: f99c0c15c44108e86a2cc8514fea0f16d323671a706209b5a05a6e1d8f297b5b8828e369483fbf1aaff70fcef37a81c43cd347656d01ca17bc66bb0cc5d441e6
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- tynn [![Build Status](https://travis-ci.org/frodsan/tynn.svg)](https://travis-ci.org/frodsan/tynn)
1
+ Tynn [![Build Status](https://travis-ci.org/frodsan/tynn.svg)](https://travis-ci.org/frodsan/tynn)
2
2
  ====
3
3
 
4
4
  A thin library for web development.
@@ -27,27 +27,24 @@ end
27
27
  run(Tynn)
28
28
  ```
29
29
 
30
- You can run `rackup` and open <http://localhost:9292/> to see the greeting
31
- message.
30
+ Check [Getting Started][start] for more information.
32
31
 
33
- Installation
34
- ------------
32
+ Documentation
33
+ -------------
35
34
 
36
- ```
37
- $ gem install tynn
38
- ```
35
+ See our website: <http://tynn.xyz/>.
39
36
 
40
37
  Contributing
41
38
  ------------
42
39
 
43
- - Fork the project.
44
- - Use `make install` to install dependencies.
45
- - Use `make test` to run the test suite.
46
- - Create a pull request with your changes.
40
+ Please see the [CONTRIBUTING][contributing] file for more information.
41
+
42
+ License
43
+ -------
47
44
 
48
- You can install the gems globally, but we recommend [gs][gs] (or
49
- [gst][gst] if you're using chruby) to keep things isolated.
45
+ Tynn is released under the [MIT License][mit].
50
46
 
47
+ [contributing]: https://github.com/frodsan/tynn/blob/master/CONTRIBUTING.md
48
+ [mit]: http://www.opensource.org/licenses/MIT
49
+ [start]: http://tynn.xyz/getting-started.html
51
50
  [syro]: http://soveran.github.io/syro/
52
- [gs]: https://github.com/soveran/gs
53
- [gst]: https://github.com/tonchis/gst
data/lib/tynn.rb CHANGED
@@ -69,7 +69,7 @@ class Tynn
69
69
  # Tynn.use(Rack::ShowExceptions)
70
70
  #
71
71
  def self.use(middleware, *args, &block)
72
- __middleware << proc { |app| middleware.new(app, *args, &block) }
72
+ self.middleware << proc { |app| middleware.new(app, *args, &block) }
73
73
  end
74
74
 
75
75
  def self.call(env) # :nodoc:
@@ -77,14 +77,15 @@ class Tynn
77
77
  end
78
78
 
79
79
  def self.build_app(syro) # :nodoc:
80
- if __middleware.empty?
80
+ if middleware.empty?
81
81
  @app = syro
82
82
  else
83
- @app = __middleware.reverse.inject(syro) { |a, m| m.call(a) }
83
+ @app = middleware.reverse.inject(syro) { |a, m| m.call(a) }
84
84
  end
85
85
  end
86
86
 
87
- def self.__middleware # :nodoc:
87
+ # Internal: Returns middleware stack.
88
+ def self.middleware
88
89
  return @middleware ||= []
89
90
  end
90
91
 
@@ -26,27 +26,19 @@ class Tynn
26
26
  #
27
27
  # If the +:ssl+ option is +true+, includes:
28
28
  #
29
- # - Tynn::HSTS
30
- #
31
- # - Tynn::ForceSSL
29
+ # - Tynn::SSL
32
30
  #
33
31
  module Protection
34
32
  # Internal: Configures security related plugins.
35
- def self.setup(app, ssl: false, force_ssl: ssl, hsts: {})
33
+ def self.setup(app, ssl: false, hsts: {})
36
34
  app.plugin(Tynn::SecureHeaders)
37
35
 
38
36
  if ssl
39
37
  app.settings[:ssl] = true
40
38
 
41
- require_relative "hsts"
42
-
43
- app.plugin(Tynn::HSTS, hsts)
44
- end
45
-
46
- if force_ssl
47
- require_relative "force_ssl"
39
+ require_relative "ssl"
48
40
 
49
- app.plugin(Tynn::ForceSSL)
41
+ app.plugin(Tynn::SSL, hsts: hsts)
50
42
  end
51
43
  end
52
44
  end
data/lib/tynn/session.rb CHANGED
@@ -62,9 +62,11 @@ class Tynn
62
62
  module Session
63
63
  # Internal: Configures Rack::Session::Cookie middleware.
64
64
  def self.setup(app, options = {})
65
- defaults = { secure: app.settings[:ssl] }
65
+ if app.settings[:ssl]
66
+ options = { secure: true }.merge(options)
67
+ end
66
68
 
67
- app.use(Rack::Session::Cookie, defaults.merge(options))
69
+ app.use(Rack::Session::Cookie, options)
68
70
  end
69
71
 
70
72
  module InstanceMethods
@@ -78,7 +80,7 @@ class Tynn
78
80
  # session[:foo] # => "foo"
79
81
  #
80
82
  def session
81
- return env["rack.session".freeze]
83
+ return req.session
82
84
  end
83
85
  end
84
86
  end
data/lib/tynn/ssl.rb ADDED
@@ -0,0 +1,115 @@
1
+ class Tynn
2
+ # Public: Enforces secure HTTP requests by:
3
+ #
4
+ # 1. Redirecting HTTP requests to their HTTPS counterparts.
5
+ #
6
+ # 2. Setting the +Strict-Transport-Security+ header. This ensures the
7
+ # browser never visits the http version of a website. This reduces
8
+ # the impact of leaking session data through cookies and external
9
+ # links, and defends against Man-in-the-middle attacks.
10
+ #
11
+ # Examples
12
+ #
13
+ # require "tynn"
14
+ # require "tynn/ssl"
15
+ # require "tynn/test"
16
+ #
17
+ # Tynn.plugin(Tynn::SSL)
18
+ #
19
+ # Tynn.define { }
20
+ #
21
+ # app = Tynn::Test.new
22
+ # app.get("/", {}, "HTTP_HOST" => "tynn.xyz")
23
+ #
24
+ # app.res.headers["Location"]
25
+ # # => "https://tynn.xyz/"
26
+ #
27
+ # You can configure HSTS with <tt>{ hsts: { ... } }</tt>. It supports the
28
+ # following options:
29
+ #
30
+ # expires - The time, in seconds, that the browser access the site only
31
+ # by HTTPS. Defaults to 180 days.
32
+ # subdomains - If this is +true+, the rule applies to all the site's
33
+ # subdomains as well. Defaults to +true+.
34
+ # preload - A limitation of HSTS is that the initial request remains
35
+ # unprotected if it uses HTTP. The same applies to the first
36
+ # request after the activity period specified by +max-age+.
37
+ # Modern browsers implements a "STS preloaded list", which
38
+ # contains known sites supporting HSTS. If you would like to
39
+ # include your website into the list, set this options to +true+
40
+ # and submit your domain to this {form}[https://hstspreload.appspot.com/].
41
+ # Supported by Chrome, Firefox, IE11+ and IE Edge.
42
+ #
43
+ # Examples
44
+ #
45
+ # Tynn.plugin(
46
+ # Tynn::SSL,
47
+ # hsts: {
48
+ # expires: 31_536_000,
49
+ # includeSubdomains: true,
50
+ # preload: true
51
+ # }
52
+ # )
53
+ #
54
+ # app = Tynn::Test.new
55
+ # app.get("/", {}, "HTTPS" => "on")
56
+ #
57
+ # app.res.headers["Strict-Transport-Security"]
58
+ # # => "max-age=31536000; includeSubdomains; preload"
59
+ #
60
+ # To disable HSTS, you will need to tell the browser to expire it
61
+ # immediately.
62
+ #
63
+ # Examples
64
+ #
65
+ # Tynn.plugin(Tynn::SSL, hsts: { expires: 0 })
66
+ #
67
+ class SSL
68
+ def self.setup(app, hsts: {}) # :nodoc:
69
+ app.use(self, hsts: hsts)
70
+ end
71
+
72
+ def initialize(app, hsts: {}) # :nodoc:
73
+ @app = app
74
+ @hsts_header = build_hsts_header(hsts)
75
+ end
76
+
77
+ def call(env) # :nodoc:
78
+ request = Rack::Request.new(env)
79
+
80
+ if request.ssl?
81
+ response = @app.call(env)
82
+
83
+ set_hsts_header!(response[1])
84
+
85
+ return response
86
+ else
87
+ return [301, redirect_headers(request), []]
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def build_hsts_header(options)
94
+ header = sprintf("max-age=%i", options.fetch(:expires, 15_552_000))
95
+ header << "; includeSubdomains" if options.fetch(:subdomains, true)
96
+ header << "; preload" if options[:preload]
97
+
98
+ return header
99
+ end
100
+
101
+ def set_hsts_header!(headers)
102
+ headers["Strict-Transport-Security".freeze] ||= @hsts_header
103
+ end
104
+
105
+ def redirect_headers(request)
106
+ return { "Location" => https_location(request) }
107
+ end
108
+
109
+ HTTPS_LOCATION = "https://%s%s".freeze
110
+
111
+ def https_location(request)
112
+ return sprintf(HTTPS_LOCATION, request.host, request.fullpath)
113
+ end
114
+ end
115
+ end
data/lib/tynn/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  class Tynn # :nodoc: all
2
2
  VERSION = [
3
3
  MAJOR_VERSION = 1,
4
- MINOR_VERSION = 2,
4
+ MINOR_VERSION = 3,
5
5
  PATCH_VERSION = 0,
6
6
  PRE_VERSION = nil
7
7
  ].compact.join(".")
data/test/ssl_test.rb ADDED
@@ -0,0 +1,60 @@
1
+ require_relative "../lib/tynn/ssl"
2
+
3
+ setup do
4
+ Tynn::Test.new
5
+ end
6
+
7
+ test "redirects to https" do |app|
8
+ Tynn.plugin(Tynn::SSL)
9
+
10
+ Tynn.define do
11
+ end
12
+
13
+ app.get("/")
14
+
15
+ assert_equal 301, app.res.status
16
+ assert_equal "https://example.org/", app.res.location
17
+ end
18
+
19
+ test "https request" do |app|
20
+ Tynn.plugin(Tynn::SSL)
21
+
22
+ Tynn.define do
23
+ root do
24
+ res.write("secure")
25
+ end
26
+ end
27
+
28
+ app.get("/", {}, "HTTPS" => "on")
29
+
30
+ assert_equal 200, app.res.status
31
+ assert_equal "secure", app.res.body
32
+ end
33
+
34
+ test "hsts header" do |app|
35
+ Tynn.plugin(Tynn::SSL)
36
+
37
+ Tynn.define do
38
+ end
39
+
40
+ app = Tynn::Test.new
41
+ app.get("/", {}, "HTTPS" => "on")
42
+
43
+ header = app.res.headers["Strict-Transport-Security"]
44
+
45
+ assert_equal "max-age=15552000; includeSubdomains", header
46
+ end
47
+
48
+ test "hsts header options" do |app|
49
+ Tynn.plugin(Tynn::SSL, hsts: { expires: 1, subdomains: false, preload: true })
50
+
51
+ Tynn.define do
52
+ end
53
+
54
+ app = Tynn::Test.new
55
+ app.get("/", {}, "HTTPS" => "on")
56
+
57
+ header = app.res.headers["Strict-Transport-Security"]
58
+
59
+ assert_equal "max-age=1; preload", header
60
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tynn
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Rodríguez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-30 00:00:00.000000000 Z
11
+ date: 2015-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -106,11 +106,8 @@ files:
106
106
  - lib/tynn.rb
107
107
  - lib/tynn/all_methods.rb
108
108
  - lib/tynn/environment.rb
109
- - lib/tynn/force_ssl.rb
110
109
  - lib/tynn/hmote.rb
111
- - lib/tynn/hsts.rb
112
110
  - lib/tynn/json.rb
113
- - lib/tynn/matchers.rb
114
111
  - lib/tynn/not_found.rb
115
112
  - lib/tynn/protection.rb
116
113
  - lib/tynn/render.rb
@@ -119,6 +116,7 @@ files:
119
116
  - lib/tynn/secure_headers.rb
120
117
  - lib/tynn/session.rb
121
118
  - lib/tynn/settings.rb
119
+ - lib/tynn/ssl.rb
122
120
  - lib/tynn/static.rb
123
121
  - lib/tynn/test.rb
124
122
  - lib/tynn/version.rb
@@ -126,18 +124,16 @@ files:
126
124
  - test/core_test.rb
127
125
  - test/default_headers_test.rb
128
126
  - test/environment_test.rb
129
- - test/force_ssl_test.rb
130
127
  - test/helper.rb
131
128
  - test/hmote_test.rb
132
- - test/hsts_test.rb
133
129
  - test/json_test.rb
134
- - test/matchers_test.rb
135
130
  - test/middleware_test.rb
136
131
  - test/not_found_test.rb
137
132
  - test/protection_test.rb
138
133
  - test/render_test.rb
139
134
  - test/secure_headers_test.rb
140
135
  - test/session_test.rb
136
+ - test/ssl_test.rb
141
137
  - test/static_test.rb
142
138
  homepage: https://github.com/frodsan/tynn
143
139
  licenses:
@@ -168,17 +164,15 @@ test_files:
168
164
  - test/core_test.rb
169
165
  - test/default_headers_test.rb
170
166
  - test/environment_test.rb
171
- - test/force_ssl_test.rb
172
167
  - test/helper.rb
173
168
  - test/hmote_test.rb
174
- - test/hsts_test.rb
175
169
  - test/json_test.rb
176
- - test/matchers_test.rb
177
170
  - test/middleware_test.rb
178
171
  - test/not_found_test.rb
179
172
  - test/protection_test.rb
180
173
  - test/render_test.rb
181
174
  - test/secure_headers_test.rb
182
175
  - test/session_test.rb
176
+ - test/ssl_test.rb
183
177
  - test/static_test.rb
184
178
  has_rdoc:
@@ -1,55 +0,0 @@
1
- class Tynn
2
- # Public: HTTP requests are permanently redirected to their HTTPS
3
- # counterparts.
4
- #
5
- # Examples
6
- #
7
- # require "tynn"
8
- # require "tynn/force_ssl"
9
- # require "tynn/test"
10
- #
11
- # Tynn.plugin(Tynn::ForceSSL)
12
- #
13
- # Tynn.define { }
14
- #
15
- # app = Tynn::Test.new
16
- # app.get("/", {}, "HTTP_HOST" => "tynn.xyz")
17
- #
18
- # app.res.headers["Location"]
19
- # # => "https://tynn.xyz/"
20
- #
21
- module ForceSSL
22
- # Internal: Sets the HTTPS redirect middleware.
23
- def self.setup(app)
24
- app.use(Tynn::ForceSSL::Middleware)
25
- end
26
-
27
- class Middleware # :nodoc:
28
- def initialize(app)
29
- @app = app
30
- end
31
-
32
- def call(env)
33
- request = Rack::Request.new(env)
34
-
35
- if request.ssl?
36
- return @app.call(env)
37
- else
38
- return [301, redirect_headers(request), []]
39
- end
40
- end
41
-
42
- private
43
-
44
- def redirect_headers(request)
45
- return { "Location" => https_location(request) }
46
- end
47
-
48
- HTTPS_LOCATION = "https://%s%s".freeze
49
-
50
- def https_location(request)
51
- return sprintf(HTTPS_LOCATION, request.host, request.fullpath)
52
- end
53
- end
54
- end
55
- end
data/lib/tynn/hsts.rb DELETED
@@ -1,64 +0,0 @@
1
- class Tynn
2
- # Public: Sets the +Strict-Transport-Security+ header. This ensures the
3
- # browser never visits the http version of a website. This reduces the
4
- # impact of leaking session data through cookies and external links, and
5
- # defends against Man-in-the-middle attacks.
6
- #
7
- # Examples
8
- #
9
- # require "tynn"
10
- # require "tynn/hsts"
11
- #
12
- # Tynn.plugin(Tynn::HSTS)
13
- #
14
- # Tynn.define { }
15
- #
16
- # Tynn.call("PATH_INFO" => "/")[1]["Strict-Transport-Security"]
17
- # # => "max-age=15552000; includeSubdomains"
18
- #
19
- # It supports the following options:
20
- #
21
- # expires - The time, in seconds, that the browser access the site only
22
- # by HTTPS. Defaults to 180 days.
23
- # subdomains - If this is +true+, the rule applies to all the site's
24
- # subdomains as well. Defaults to +true+.
25
- # preload - A limitation of HSTS is that the initial request remains
26
- # unprotected if it uses HTTP. The same applies to the first
27
- # request after the activity period specified by +max-age+.
28
- # Modern browsers implements a "STS preloaded list", which
29
- # contains known sites supporting HSTS. If you would like to
30
- # include your website into the list, set this options to +true+
31
- # and submit your domain to this {form}[https://hstspreload.appspot.com/].
32
- # Supported by Chrome, Firefox, IE11+ and IE Edge.
33
- #
34
- # Examples
35
- #
36
- # Tynn.plugin(
37
- # Tynn::HSTS,
38
- # expires: 31_536_000,
39
- # includeSubdomains: true,
40
- # preload: true
41
- # )
42
- #
43
- # Tynn.define { }
44
- #
45
- # Tynn.call("PATH_INFO" => "/")[1]["Strict-Transport-Security"]
46
- # # => "max-age=31536000; includeSubdomains; preload"
47
- #
48
- # To disable HSTS, you will need to tell the browser to expire it immediately.
49
- #
50
- # Examples
51
- #
52
- # Tynn.plugin(Tynn::HSTS, expires: 0)
53
- #
54
- module HSTS
55
- # Internal: Sets the HSTS header as a default header.
56
- def self.setup(app, options = {})
57
- header = sprintf("max-age=%i", options.fetch(:expires, 15_552_000))
58
- header << "; includeSubdomains" if options.fetch(:subdomains, true)
59
- header << "; preload" if options[:preload]
60
-
61
- app.settings[:default_headers]["Strict-Transport-Security"] = header
62
- end
63
- end
64
- end
data/lib/tynn/matchers.rb DELETED
@@ -1,60 +0,0 @@
1
- class Tynn
2
- # Public: Adds extra matchers to Tynn.
3
- #
4
- # Examples
5
- #
6
- # require "tynn"
7
- # require "tynn/matchers"
8
- #
9
- # Tynn.plugin(Tynn::Matchers)
10
- #
11
- module Matchers
12
- module InstanceMethods
13
- # Public: A catch-all matcher. Always executes the given block.
14
- #
15
- # Examples
16
- #
17
- # Tynn.define do
18
- # authenticated? do
19
- # # ...
20
- # end
21
- #
22
- # default do # on true
23
- # # ...
24
- # end
25
- # end
26
- #
27
- def default
28
- yield
29
-
30
- halt(res.finish)
31
- end
32
-
33
- # Public: Executes the given block if +key+ is present in +req.params+.
34
- #
35
- # key - Any object that responds to +to_s+.
36
- #
37
- # Examples
38
- #
39
- # Tynn.define do
40
- # param(:user) do |params|
41
- # user = User.create(params)
42
- #
43
- # # ...
44
- # end
45
- #
46
- # default do
47
- # res.write("missing [user] param")
48
- # end
49
- # end
50
- #
51
- def param(key)
52
- if (v = req[key]) && !v.empty?
53
- yield(v)
54
-
55
- halt(res.finish)
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,32 +0,0 @@
1
- require_relative "../lib/tynn/force_ssl"
2
-
3
- setup do
4
- Tynn::Test.new
5
- end
6
-
7
- test "redirects to https" do |app|
8
- Tynn.plugin(Tynn::ForceSSL)
9
-
10
- Tynn.define do
11
- end
12
-
13
- app.get("/")
14
-
15
- assert_equal 301, app.res.status
16
- assert_equal "https://example.org/", app.res.location
17
- end
18
-
19
- test "https request" do |app|
20
- Tynn.plugin(Tynn::ForceSSL)
21
-
22
- Tynn.define do
23
- root do
24
- res.write("secure")
25
- end
26
- end
27
-
28
- app.get("/", {}, "HTTPS" => "on")
29
-
30
- assert_equal 200, app.res.status
31
- assert_equal "secure", app.res.body
32
- end
data/test/hsts_test.rb DELETED
@@ -1,29 +0,0 @@
1
- require_relative "../lib/tynn/hsts"
2
-
3
- test "hsts header" do |app|
4
- Tynn.plugin(Tynn::HSTS)
5
-
6
- Tynn.define do
7
- end
8
-
9
- app = Tynn::Test.new
10
- app.get("/", {})
11
-
12
- header = app.res.headers["Strict-Transport-Security"]
13
-
14
- assert_equal "max-age=15552000; includeSubdomains", header
15
- end
16
-
17
- test "hsts header options" do |app|
18
- Tynn.plugin(Tynn::HSTS, expires: 1, subdomains: false, preload: true)
19
-
20
- Tynn.define do
21
- end
22
-
23
- app = Tynn::Test.new
24
- app.get("/", {})
25
-
26
- header = app.res.headers["Strict-Transport-Security"]
27
-
28
- assert_equal "max-age=1; preload", header
29
- end
@@ -1,40 +0,0 @@
1
- require_relative "../lib/tynn/matchers"
2
-
3
- setup do
4
- Tynn.plugin(Tynn::Matchers)
5
-
6
- Tynn::Test.new
7
- end
8
-
9
- test "default" do |app|
10
- Tynn.define do
11
- default do
12
- res.write("foo")
13
- end
14
- end
15
-
16
- app.get("/")
17
-
18
- assert_equal 200, app.res.status
19
- assert_equal "foo", app.res.body
20
- end
21
-
22
- test "param" do |app|
23
- Tynn.define do
24
- param(:foo) do |foo|
25
- res.write(foo)
26
- end
27
-
28
- param("bar") do |bar|
29
- res.write(bar)
30
- end
31
- end
32
-
33
- app.get("/", foo: "foo")
34
-
35
- assert_equal "foo", app.res.body
36
-
37
- app.get("/", foo: "bar")
38
-
39
- assert_equal "bar", app.res.body
40
- end