tynn 1.2.0 → 1.3.0

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.
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