tynn 1.0.0.rc1 → 1.0.0.rc2

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.
@@ -1,13 +1,42 @@
1
1
  require_relative "secure_headers"
2
2
 
3
- module Tynn::Protection
4
- def self.setup(app, ssl: false, hsts: {}) # :nodoc:
5
- app.helpers(Tynn::SecureHeaders)
3
+ class Tynn
4
+ # Adds security measures against common attacks.
5
+ #
6
+ # ```
7
+ # require "tynn"
8
+ # require "tynn/protection"
9
+ #
10
+ # Tynn.helpers(Tynn::Protection)
11
+ # ```
12
+ #
13
+ # If you are using SSL/TLS (HTTPS), it's recommended to set
14
+ # the `:ssl` option:
15
+ #
16
+ # ```
17
+ # require "tynn"
18
+ # require "tynn/protection"
19
+ #
20
+ # Tynn.helpers(Tynn::Protection, ssl: true)
21
+ # ```
22
+ #
23
+ # By default, it includes the following security helpers:
24
+ #
25
+ # * Tynn::SecureHeaders
26
+ #
27
+ # If the `:ssl` option is `true`, includes:
28
+ #
29
+ # * Tynn::SSL
30
+ #
31
+ module Protection
32
+ def self.setup(app, ssl: false, hsts: {}) # :nodoc:
33
+ app.helpers(Tynn::SecureHeaders)
6
34
 
7
- if ssl
8
- require_relative "ssl"
35
+ if ssl
36
+ require_relative "ssl"
9
37
 
10
- app.helpers(Tynn::SSL, hsts: hsts)
38
+ app.helpers(Tynn::SSL, hsts: hsts)
39
+ end
11
40
  end
12
41
  end
13
42
  end
@@ -1,56 +1,48 @@
1
1
  require "tilt"
2
2
 
3
- module Tynn::Render
4
- def self.setup(app, options = {}) # :nodoc:
5
- options = options.dup
6
-
7
- options[:engine] ||= "erb"
8
- options[:layout] ||= "layout"
9
- options[:views] ||= File.expand_path("views", Dir.pwd)
10
-
11
- options[:options] ||= {}
12
- options[:options] = {
13
- default_encoding: Encoding.default_external,
14
- outvar: "@_output"
15
- }.merge!(options[:options])
16
-
17
- app.settings[:render] ||= options
18
- end
19
-
20
- module ClassMethods
21
- def layout(layout)
22
- settings[:render][:layout] = layout
3
+ class Tynn
4
+ module Render
5
+ def self.setup(app, options = {}) # :nodoc:
6
+ app.settings.update(
7
+ layout: options.fetch(:layout, "layout"),
8
+ views: options.fetch(:views, File.expand_path("views", Dir.pwd)),
9
+ engine: options.fetch(:engine, "erb"),
10
+ engine_opts: {
11
+ default_encoding: Encoding.default_external,
12
+ outvar: "@_output"
13
+ }.merge!(options.fetch(:options, {}))
14
+ )
23
15
  end
24
- end
25
16
 
26
- def render(template, locals = {}, layout = settings[:render][:layout])
27
- res.headers[Rack::CONTENT_TYPE] ||= Syro::Response::DEFAULT
17
+ def render(template, locals = {}, layout = settings[:layout])
18
+ res.headers[Rack::CONTENT_TYPE] ||= Syro::Response::DEFAULT
28
19
 
29
- res.write(view(template, locals, layout))
30
- end
20
+ res.write(view(template, locals, layout))
21
+ end
31
22
 
32
- def view(template, locals = {}, layout = settings[:render][:layout])
33
- return partial(layout, locals.merge(content: partial(template, locals)))
34
- end
23
+ def view(template, locals = {}, layout = settings[:layout])
24
+ return partial(layout, locals.merge(content: partial(template, locals)))
25
+ end
35
26
 
36
- def partial(template, locals = {})
37
- return tilt(template_path(template), locals, settings[:render][:options])
38
- end
27
+ def partial(template, locals = {})
28
+ return tilt(template_path(template), locals, settings[:engine_opts])
29
+ end
39
30
 
40
- private
31
+ private
41
32
 
42
- def tilt(file, locals = {}, opts = {})
43
- return tilt_cache.fetch(file) { Tilt.new(file, 1, opts) }.render(self, locals)
44
- end
33
+ def tilt(file, locals = {}, opts = {})
34
+ return tilt_cache.fetch(file) { Tilt.new(file, 1, opts) }.render(self, locals)
35
+ end
45
36
 
46
- def tilt_cache
47
- return Thread.current[:tilt_cache] ||= Tilt::Cache.new
48
- end
37
+ def tilt_cache
38
+ return Thread.current[:tilt_cache] ||= Tilt::Cache.new
39
+ end
49
40
 
50
- def template_path(template)
51
- dir = settings[:render][:views]
52
- ext = settings[:render][:engine]
41
+ def template_path(template)
42
+ dir = settings[:views]
43
+ ext = settings[:engine]
53
44
 
54
- return File.join(dir, "#{ template }.#{ ext }")
45
+ return File.join(dir, "#{ template }.#{ ext }")
46
+ end
55
47
  end
56
48
  end
@@ -1,4 +1,6 @@
1
1
  class Tynn
2
+ # It provides convenience methods for pulling out information
3
+ # from a request.
2
4
  class Request < Rack::Request
3
5
  end
4
6
  end
@@ -1,5 +1,39 @@
1
1
  class Tynn
2
+ # It provides convenience methods to construct a Rack response.
3
+ #
4
+ # ```
5
+ # res = Tynn::Response.new
6
+ # res.status = 200
7
+ # res["Content-Type"] = "text/html"
8
+ # res.write("foo")
9
+ # ```
10
+ #
11
+ # [Tynn::Response#finish][finish] returns a response as per
12
+ # [Rack's specification][rack-spec].
13
+ #
14
+ # ```
15
+ # res.finish
16
+ # # => [200, { "Content-Type" => "text/html", "Content-Length" => 3 }, ["foo"]]
17
+ # ```
18
+ #
19
+ # [finish]: #method-i-finish
20
+ # [rack-spec]: http://www.rubydoc.info/github/rack/rack/master/file/SPEC
21
+ #
2
22
  class Response < Syro::Response
23
+ ##
24
+ # :method: new
25
+ # :call-seq: new(headers = {})
26
+ #
27
+ # Initializes a new response object with the given `headers`.
28
+ #
29
+ # ```
30
+ # Tynn::Response.new.headers
31
+ # # => {}
32
+ #
33
+ # Tynn::Response.new("Content-Type" => "text/plain").headers
34
+ # # => { "Content-Type" => "text/plain" }
35
+ # ```
36
+
3
37
  ##
4
38
  # :method: []
5
39
  #
@@ -48,9 +82,9 @@ class Tynn
48
82
  # # => [404, {}, []]
49
83
  #
50
84
  # res.status = nil
51
- # res.write("yo!")
85
+ # res.write("yo")
52
86
  # res.finish
53
- # # => [200, { "Content-Type" => "text/html" }, ["yo!"]]
87
+ # # => [200, { "Content-Type" => "text/html", "Content-Length" => 2 }, ["yo"]]
54
88
 
55
89
  ##
56
90
  # :method: headers
@@ -1,43 +1,45 @@
1
- # Adds security related HTTP headers.
2
- #
3
- # ```
4
- # require "tynn"
5
- # require "tynn/secure_headers"
6
- #
7
- # Tynn.helpers(Tynn::SecureHeaders)
8
- # ```
9
- #
10
- # This helper applies the following headers:
11
- #
12
- # * **X-Content-Type-Options:** Prevents IE and Chrome from
13
- # [content type sniffing][mime-sniffing].
14
- #
15
- # * **X-Frame-Options (XFO):** Provides [Clickjacking][clickjacking]
16
- # protection. Check the [X-Frame-Options draft][x-frame-options] for
17
- # more information.
18
- #
19
- # * **X-Permitted-Cross-Domain-Policies:** Restricts Adobe Flash Player's
20
- # access to data. Check this [article][pcdp] for more information.
21
- #
22
- # * **X-XSS-Protection:** Enables the [XSS][xss] protection filter built
23
- # into IE, Chrome and Safari. This filter is usually enabled by default,
24
- # the use of this header is to re-enable it if it was disabled by the user.
25
- #
26
- # [clickjacking]: https://www.owasp.org/index.php/Clickjacking
27
- # [mime-sniffing]: https://msdn.microsoft.com/library/gg622941(v=vs.85).aspx
28
- # [pcdp]: https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
29
- # [x-frame-options]: https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-02
30
- # [xss]: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
31
- #
32
- module Tynn::SecureHeaders
33
- HEADERS = {
34
- "X-Content-Type-Options" => "nosniff",
35
- "X-Frame-Options" => "SAMEORIGIN",
36
- "X-Permitted-Cross-Domain-Policies" => "none",
37
- "X-XSS-Protection" => "1; mode=block"
38
- } # :nodoc:
1
+ class Tynn
2
+ # Adds security related HTTP headers.
3
+ #
4
+ # ```
5
+ # require "tynn"
6
+ # require "tynn/secure_headers"
7
+ #
8
+ # Tynn.helpers(Tynn::SecureHeaders)
9
+ # ```
10
+ #
11
+ # This helper applies the following headers:
12
+ #
13
+ # * **X-Content-Type-Options:** Prevents IE and Chrome from
14
+ # [content type sniffing][mime-sniffing].
15
+ #
16
+ # * **X-Frame-Options (XFO):** Provides [Clickjacking][clickjacking]
17
+ # protection. Check the [X-Frame-Options draft][x-frame-options] for
18
+ # more information.
19
+ #
20
+ # * **X-Permitted-Cross-Domain-Policies:** Restricts Adobe Flash Player's
21
+ # access to data. Check this [article][pcdp] for more information.
22
+ #
23
+ # * **X-XSS-Protection:** Enables the [XSS][xss] protection filter built
24
+ # into IE, Chrome and Safari. This filter is usually enabled by default,
25
+ # the use of this header is to re-enable it if it was disabled by the user.
26
+ #
27
+ # [clickjacking]: https://www.owasp.org/index.php/Clickjacking
28
+ # [mime-sniffing]: https://msdn.microsoft.com/library/gg622941(v=vs.85).aspx
29
+ # [pcdp]: https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
30
+ # [x-frame-options]: https://tools.ietf.org/html/draft-ietf-websec-x-frame-options-02
31
+ # [xss]: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
32
+ #
33
+ module SecureHeaders
34
+ HEADERS = {
35
+ "X-Content-Type-Options" => "nosniff",
36
+ "X-Frame-Options" => "SAMEORIGIN",
37
+ "X-Permitted-Cross-Domain-Policies" => "none",
38
+ "X-XSS-Protection" => "1; mode=block"
39
+ } # :nodoc:
39
40
 
40
- def default_headers # :nodoc:
41
- return super.merge(HEADERS)
41
+ def default_headers # :nodoc:
42
+ return super.merge(HEADERS)
43
+ end
42
44
  end
43
45
  end
@@ -1,85 +1,87 @@
1
- # Adds simple cookie based session management. If a secret token is
2
- # given, it signs the cookie data to ensure that it cannot be altered
3
- # by unauthorized means.
4
- #
5
- # ```
6
- # require "tynn"
7
- # require "tynn/session"
8
- #
9
- # Tynn.helpers(Tynn::Session, secret: "__change_me__")
10
- #
11
- # Tynn.define do
12
- # root do
13
- # res.write(sprintf("hei %s", session[:username]))
14
- # end
15
- #
16
- # on(:username) do |username|
17
- # session[:username] = username
18
- # end
19
- # end
20
- # ```
21
- #
22
- # The following command generates a cryptographically secure secret ready
23
- # to use:
24
- #
25
- # ```
26
- # $ ruby -r securerandom -e "puts SecureRandom.hex(64)"
27
- # ```
28
- #
29
- # It's important to keep the token secret. Knowing the token allows an
30
- # attacker to tamper the data. So, it's recommended to load the token
31
- # from the environment.
32
- #
33
- # ```
34
- # Tynn.helpers(Tynn::Session, secret: ENV["SESSION_SECRET"])
35
- # ```
36
- #
37
- # Under the hood, Tynn::Session uses the [Rack::Session::Cookie][rack-session]
38
- # middleware. Thus, supports all the options available for this middleware.
39
- #
40
- # * `:key` - the name of the cookie. Defaults to `"rack.session"`.
41
- # * `:expire_after` - sets the lifespan of the cookie. If `nil`,
42
- # the cookie will be deleted after the user close the browser.
43
- # Defaults to `nil`.
44
- # * `:httponly` - if `true`, sets the [HttpOnly][cookie-httponly] attribute.
45
- # This mitigates the risk of client side scripting accessing the cookie.
46
- # Defaults to `true`.
47
- # * `:secure` - if `true`, sets the [Secure][cookie-secure] attribute.
48
- # This tells the browser to only transmit the cookie over HTTPS. Defaults
49
- # to `false`.
50
- #
51
- # ```
52
- # Tynn.helpers(
53
- # Tynn::Session,
54
- # key: "app",
55
- # secret: ENV["SESSION_SECRET"],
56
- # expire_after: 36_000, # seconds
57
- # httponly: true,
58
- # secure: true
59
- # )
60
- # ```
61
- #
62
- # [cookie-httponly]: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#HttpOnly_Attribute
63
- # [cookie-secure]: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Secure_Attribute
64
- # [rack-session]: http://www.rubydoc.info/gems/rack/Rack/Session/Cookie
65
- #
66
- module Tynn::Session
67
- RACK_SESSION = "rack.session".freeze # :nodoc:
68
-
69
- def self.setup(app, options = {}) # :nodoc:
70
- app.use(Rack::Session::Cookie, options)
71
- end
72
-
73
- # Returns the session hash.
1
+ class Tynn
2
+ # Adds simple cookie based session management. If a secret token is
3
+ # given, it signs the cookie data to ensure that it cannot be altered
4
+ # by unauthorized means.
74
5
  #
75
6
  # ```
76
- # session # => {}
7
+ # require "tynn"
8
+ # require "tynn/session"
77
9
  #
78
- # session[:foo] = "foo"
79
- # session[:foo] # => "foo"
10
+ # Tynn.helpers(Tynn::Session, secret: "__change_me__")
11
+ #
12
+ # Tynn.define do
13
+ # root do
14
+ # res.write(sprintf("hei %s", session[:username]))
15
+ # end
16
+ #
17
+ # on(:username) do |username|
18
+ # session[:username] = username
19
+ # end
20
+ # end
80
21
  # ```
81
22
  #
82
- def session
83
- return env[RACK_SESSION]
23
+ # The following command generates a cryptographically secure secret ready
24
+ # to use:
25
+ #
26
+ # ```
27
+ # $ ruby -r securerandom -e "puts SecureRandom.hex(64)"
28
+ # ```
29
+ #
30
+ # It's important to keep the token secret. Knowing the token allows an
31
+ # attacker to tamper the data. So, it's recommended to load the token
32
+ # from the environment.
33
+ #
34
+ # ```
35
+ # Tynn.helpers(Tynn::Session, secret: ENV["SESSION_SECRET"])
36
+ # ```
37
+ #
38
+ # Under the hood, Tynn::Session uses the [Rack::Session::Cookie][rack-session]
39
+ # middleware. Thus, supports all the options available for this middleware.
40
+ #
41
+ # * `:key` - the name of the cookie. Defaults to `"rack.session"`.
42
+ # * `:expire_after` - sets the lifespan of the cookie. If `nil`,
43
+ # the cookie will be deleted after the user close the browser.
44
+ # Defaults to `nil`.
45
+ # * `:httponly` - if `true`, sets the [HttpOnly][cookie-httponly] attribute.
46
+ # This mitigates the risk of client side scripting accessing the cookie.
47
+ # Defaults to `true`.
48
+ # * `:secure` - if `true`, sets the [Secure][cookie-secure] attribute.
49
+ # This tells the browser to only transmit the cookie over HTTPS. Defaults
50
+ # to `false`.
51
+ #
52
+ # ```
53
+ # Tynn.helpers(
54
+ # Tynn::Session,
55
+ # key: "app",
56
+ # secret: ENV["SESSION_SECRET"],
57
+ # expire_after: 36_000, # seconds
58
+ # httponly: true,
59
+ # secure: true
60
+ # )
61
+ # ```
62
+ #
63
+ # [cookie-httponly]: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#HttpOnly_Attribute
64
+ # [cookie-secure]: https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Secure_Attribute
65
+ # [rack-session]: http://www.rubydoc.info/gems/rack/Rack/Session/Cookie
66
+ #
67
+ module Session
68
+ RACK_SESSION = "rack.session".freeze # :nodoc:
69
+
70
+ def self.setup(app, options = {}) # :nodoc:
71
+ app.use(Rack::Session::Cookie, options)
72
+ end
73
+
74
+ # Returns the session hash.
75
+ #
76
+ # ```
77
+ # session # => {}
78
+ #
79
+ # session[:foo] = "foo"
80
+ # session[:foo] # => "foo"
81
+ # ```
82
+ #
83
+ def session
84
+ return env[RACK_SESSION]
85
+ end
84
86
  end
85
87
  end