static-rails 0.0.6 → 0.0.11
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 +4 -4
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +16 -13
- data/README.md +3 -2
- data/lib/generators/templates/static.rb +1 -1
- data/lib/static-rails/configuration.rb +1 -1
- data/lib/static-rails/file_handler.rb +173 -0
- data/lib/static-rails/gets_csrf_token.rb +16 -0
- data/lib/static-rails/proxy_middleware.rb +0 -1
- data/lib/static-rails/request_forgery_protection_fallback.rb +19 -0
- data/lib/static-rails/server_store.rb +0 -4
- data/lib/static-rails/site_middleware.rb +3 -8
- data/lib/static-rails/site_plus_csrf_middleware.rb +16 -6
- data/lib/static-rails/static_middleware.rb +13 -10
- data/lib/static-rails/validates_csrf_token.rb +33 -0
- data/lib/static-rails/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5808adb0f3f344a5706a6dddb5053899047f5d8aa4bbddb577168319b9aa046
|
4
|
+
data.tar.gz: d84c08d618e80a28b95be9da396f34d57636985ecf83859a906c5997a1532238
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adb43e14b69561af8ce12989d319782b95d8c915b30968c4ce28d6bbf109426b8d606ba83d860002c2d793c16be3fbfe6c962be473df83507fd18d1e16de66c4
|
7
|
+
data.tar.gz: 7f4e6b9a9ed8dabaccd3a0da66671c36605a56e15da8483fe17f9518badbe3be6e2b82369ac4e7a05af9b4d594eaec1b010ad7616fc77b3aa835f81e0f87aa57
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
## 0.0.11
|
2
|
+
|
3
|
+
* Inline the `ActionDispatch::FileHandler` from Rails master so that we can
|
4
|
+
target a single stable version of its API and control what MIME types it
|
5
|
+
considers to be compressible (bonus is that it effectively backports brotli
|
6
|
+
compression to pre-6.1 rails apps)
|
7
|
+
|
8
|
+
## 0.0.10
|
9
|
+
|
10
|
+
* Change default `cache-control` header for static assets being served from disk
|
11
|
+
from `no-cache` to `"public; max-age=31536000"`
|
12
|
+
|
13
|
+
## 0.0.9
|
14
|
+
|
15
|
+
* When using CSRF protection, the artificial path info will now be
|
16
|
+
"__static_rails__" instead of a random string, to make logs appear cleaner
|
17
|
+
* Attempt to guard against future internal changes to Rails' request forgery
|
18
|
+
protection by adding `method_missing` that calls through
|
19
|
+
|
20
|
+
## 0.0.8
|
21
|
+
|
22
|
+
* Add support for the [CSRF
|
23
|
+
changes](https://github.com/rails/rails/commit/358ff18975f26e820ea355ec113ffc5228e59af8) in Rails 6.0.3.1
|
24
|
+
|
25
|
+
## 0.0.7
|
26
|
+
|
27
|
+
* Ensure that CSRF tokens are valid, at the cost of some performance and
|
28
|
+
reliance on additional Rails internals. As a result CSRF cookie setting is now
|
29
|
+
disabled by default [#6](https://github.com/testdouble/static-rails/pull/6)
|
30
|
+
|
1
31
|
## 0.0.6
|
2
32
|
|
3
33
|
* Fix an issue where `ActionDispatch::FileHandler` won't be loaded in the event
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
static-rails (0.0.
|
4
|
+
static-rails (0.0.11)
|
5
5
|
rack-proxy (~> 0.6)
|
6
6
|
railties (>= 5.0.0)
|
7
7
|
|
@@ -32,19 +32,18 @@ GEM
|
|
32
32
|
concurrent-ruby (1.1.6)
|
33
33
|
crass (1.0.6)
|
34
34
|
erubi (1.9.0)
|
35
|
-
i18n (1.8.
|
35
|
+
i18n (1.8.3)
|
36
36
|
concurrent-ruby (~> 1.0)
|
37
|
-
jaro_winkler (1.5.4)
|
38
37
|
loofah (2.5.0)
|
39
38
|
crass (~> 1.0.2)
|
40
39
|
nokogiri (>= 1.5.9)
|
41
40
|
method_source (1.0.0)
|
42
41
|
mini_portile2 (2.4.0)
|
43
|
-
minitest (5.14.
|
42
|
+
minitest (5.14.1)
|
44
43
|
nokogiri (1.10.9)
|
45
44
|
mini_portile2 (~> 2.4.0)
|
46
45
|
parallel (1.19.1)
|
47
|
-
parser (2.7.1.
|
46
|
+
parser (2.7.1.3)
|
48
47
|
ast (~> 2.4.0)
|
49
48
|
rack (2.2.2)
|
50
49
|
rack-proxy (0.6.5)
|
@@ -64,26 +63,30 @@ GEM
|
|
64
63
|
thor (>= 0.20.3, < 2.0)
|
65
64
|
rainbow (3.0.0)
|
66
65
|
rake (13.0.1)
|
66
|
+
regexp_parser (1.7.1)
|
67
67
|
rexml (3.2.4)
|
68
|
-
rubocop (0.
|
69
|
-
jaro_winkler (~> 1.5.1)
|
68
|
+
rubocop (0.85.1)
|
70
69
|
parallel (~> 1.10)
|
71
70
|
parser (>= 2.7.0.1)
|
72
71
|
rainbow (>= 2.2.2, < 4.0)
|
72
|
+
regexp_parser (>= 1.7)
|
73
73
|
rexml
|
74
|
+
rubocop-ast (>= 0.0.3)
|
74
75
|
ruby-progressbar (~> 1.7)
|
75
|
-
unicode-display_width (>= 1.4.0, <
|
76
|
-
rubocop-
|
76
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
77
|
+
rubocop-ast (0.0.3)
|
78
|
+
parser (>= 2.7.0.1)
|
79
|
+
rubocop-performance (1.6.1)
|
77
80
|
rubocop (>= 0.71.0)
|
78
81
|
ruby-progressbar (1.10.1)
|
79
|
-
standard (0.
|
80
|
-
rubocop (~> 0.
|
81
|
-
rubocop-performance (~> 1.
|
82
|
+
standard (0.4.7)
|
83
|
+
rubocop (~> 0.85.0)
|
84
|
+
rubocop-performance (~> 1.6.0)
|
82
85
|
thor (1.0.1)
|
83
86
|
thread_safe (0.3.6)
|
84
87
|
tzinfo (1.2.7)
|
85
88
|
thread_safe (~> 0.1)
|
86
|
-
unicode-display_width (1.
|
89
|
+
unicode-display_width (1.7.0)
|
87
90
|
zeitwerk (2.3.0)
|
88
91
|
|
89
92
|
PLATFORMS
|
data/README.md
CHANGED
@@ -84,13 +84,14 @@ overall behavior of the gem itself, across all your static sites:
|
|
84
84
|
`proxy_requests` is true, that the gem will wait for a response from a static
|
85
85
|
site's server on any given request before timing out and raising an error
|
86
86
|
|
87
|
-
* **config.set_csrf_token_cookie** (Default: `
|
87
|
+
* **config.set_csrf_token_cookie** (Default: `false`) when true, the gem's
|
88
88
|
middleware will set a cookie named `_csrf_token` with each request of your
|
89
89
|
static site. You can use this to set the `'x-csrf-token'` header on any
|
90
90
|
requests from your site back to routes hosted by the Rails app that are
|
91
91
|
[protected from CSRF
|
92
92
|
forgery](https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf)
|
93
|
-
(if you're not using Rails' cookie store for sessions
|
93
|
+
(if you're not using Rails' cookie store for sessions or you're okay with API
|
94
|
+
calls bypassing Rails CSRF, leave this off)
|
94
95
|
|
95
96
|
### Configuring your static sites themselves
|
96
97
|
|
@@ -12,7 +12,7 @@ StaticRails.config do |config|
|
|
12
12
|
# When true, both the proxy & static asset middleware will set a cookie
|
13
13
|
# named "_csrf_token" to the Rails CSRF token, allowing any client-side
|
14
14
|
# API requests to take advantage of Rails' request forgery protection
|
15
|
-
# config.set_csrf_token_cookie =
|
15
|
+
# config.set_csrf_token_cookie = false
|
16
16
|
|
17
17
|
# The list of static sites you are hosting with static-rails.
|
18
18
|
# Note that order matters! Request will be forwarded to the first site that
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module StaticRails
|
2
|
+
# This class was extracted from Ruby on Rails:
|
3
|
+
#
|
4
|
+
# - actionpack/lib/action_dispatch/middleware/static.rb
|
5
|
+
#
|
6
|
+
# Copyright (c) 2005-2020 David Heinemeier Hansson, Ryan Edward Hall, Jeremy Daer
|
7
|
+
#
|
8
|
+
# License here: https://github.com/rails/rails/blob/master/MIT-LICENSE
|
9
|
+
#
|
10
|
+
# This endpoint serves static files from disk using Rack::File.
|
11
|
+
#
|
12
|
+
# URL paths are matched with static files according to expected
|
13
|
+
# conventions: +path+, +path+.html, +path+/index.html.
|
14
|
+
#
|
15
|
+
# Precompressed versions of these files are checked first. Brotli (.br)
|
16
|
+
# and gzip (.gz) files are supported. If +path+.br exists, this
|
17
|
+
# endpoint returns that file with a +Content-Encoding: br+ header.
|
18
|
+
#
|
19
|
+
# If no matching file is found, this endpoint responds 404 Not Found.
|
20
|
+
#
|
21
|
+
# Pass the +root+ directory to search for matching files, an optional
|
22
|
+
# +index: "index"+ to change the default +path+/index.html, and optional
|
23
|
+
# additional response headers.
|
24
|
+
class FileHandler
|
25
|
+
# Accept-Encoding value -> file extension
|
26
|
+
PRECOMPRESSED = {
|
27
|
+
"br" => ".br",
|
28
|
+
"gzip" => ".gz",
|
29
|
+
"identity" => nil
|
30
|
+
}
|
31
|
+
|
32
|
+
def initialize(root, index: "index", headers: {}, precompressed: %i[br gzip], compressible_content_types: /\A(?:text\/|application\/javascript)/)
|
33
|
+
@root = root.chomp("/").b
|
34
|
+
@index = index
|
35
|
+
|
36
|
+
@precompressed = Array(precompressed).map(&:to_s) | %w[identity]
|
37
|
+
@compressible_content_types = compressible_content_types
|
38
|
+
|
39
|
+
@file_server = ::Rack::File.new(@root, headers)
|
40
|
+
end
|
41
|
+
|
42
|
+
def call(env)
|
43
|
+
attempt(env) || @file_server.call(env)
|
44
|
+
end
|
45
|
+
|
46
|
+
def attempt(env)
|
47
|
+
request = Rack::Request.new env
|
48
|
+
|
49
|
+
if request.get? || request.head?
|
50
|
+
if (found = find_file(request.path_info, accept_encoding: request.accept_encoding))
|
51
|
+
serve request, *found
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def serve(request, filepath, content_headers)
|
57
|
+
original, request.path_info =
|
58
|
+
request.path_info, ::Rack::Utils.escape_path(filepath).b
|
59
|
+
|
60
|
+
@file_server.call(request.env).tap do |status, headers, body|
|
61
|
+
# Omit Content-Encoding/Type/etc headers for 304 Not Modified
|
62
|
+
if status != 304
|
63
|
+
headers.update(content_headers)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
ensure
|
67
|
+
request.path_info = original
|
68
|
+
end
|
69
|
+
|
70
|
+
# Match a URI path to a static file to be served.
|
71
|
+
#
|
72
|
+
# Used by the +Static+ class to negotiate a servable file in the
|
73
|
+
# +public/+ directory (see Static#call).
|
74
|
+
#
|
75
|
+
# Checks for +path+, +path+.html, and +path+/index.html files,
|
76
|
+
# in that order, including .br and .gzip compressed extensions.
|
77
|
+
#
|
78
|
+
# If a matching file is found, the path and necessary response headers
|
79
|
+
# (Content-Type, Content-Encoding) are returned.
|
80
|
+
def find_file(path_info, accept_encoding:)
|
81
|
+
each_candidate_filepath(path_info) do |filepath, content_type|
|
82
|
+
if (response = try_files(filepath, content_type, accept_encoding: accept_encoding))
|
83
|
+
return response
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def try_files(filepath, content_type, accept_encoding:)
|
91
|
+
headers = {"Content-Type" => content_type}
|
92
|
+
|
93
|
+
if compressible? content_type
|
94
|
+
try_precompressed_files filepath, headers, accept_encoding: accept_encoding
|
95
|
+
elsif file_readable? filepath
|
96
|
+
[filepath, headers]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def try_precompressed_files(filepath, headers, accept_encoding:)
|
101
|
+
each_precompressed_filepath(filepath) do |content_encoding, precompressed_filepath|
|
102
|
+
if file_readable? precompressed_filepath
|
103
|
+
# Identity encoding is default, so we skip Accept-Encoding
|
104
|
+
# negotiation and needn't set Content-Encoding.
|
105
|
+
#
|
106
|
+
# Vary header is expected when we've found other available
|
107
|
+
# encodings that Accept-Encoding ruled out.
|
108
|
+
if content_encoding == "identity"
|
109
|
+
return precompressed_filepath, headers
|
110
|
+
else
|
111
|
+
headers["Vary"] = "Accept-Encoding"
|
112
|
+
|
113
|
+
if accept_encoding.any? { |enc, _| /\b#{content_encoding}\b/i.match?(enc) }
|
114
|
+
headers["Content-Encoding"] = content_encoding
|
115
|
+
return precompressed_filepath, headers
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def file_readable?(path)
|
123
|
+
file_stat = File.stat(File.join(@root, path.b))
|
124
|
+
rescue SystemCallError
|
125
|
+
false
|
126
|
+
else
|
127
|
+
file_stat.file? && file_stat.readable?
|
128
|
+
end
|
129
|
+
|
130
|
+
def compressible?(content_type)
|
131
|
+
@compressible_content_types.match?(content_type)
|
132
|
+
end
|
133
|
+
|
134
|
+
def each_precompressed_filepath(filepath)
|
135
|
+
@precompressed.each do |content_encoding|
|
136
|
+
precompressed_ext = PRECOMPRESSED.fetch(content_encoding)
|
137
|
+
yield content_encoding, "#{filepath}#{precompressed_ext}"
|
138
|
+
end
|
139
|
+
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
|
143
|
+
def each_candidate_filepath(path_info)
|
144
|
+
return unless (path = clean_path(path_info))
|
145
|
+
|
146
|
+
ext = ::File.extname(path)
|
147
|
+
content_type = ::Rack::Mime.mime_type(ext, nil)
|
148
|
+
yield path, content_type || "text/plain"
|
149
|
+
|
150
|
+
# Tack on .html and /index.html only for paths that don't have
|
151
|
+
# an explicit, resolvable file extension. No need to check
|
152
|
+
# for foo.js.html and foo.js/index.html.
|
153
|
+
unless content_type
|
154
|
+
default_ext = ::ActionController::Base.default_static_extension
|
155
|
+
if ext != default_ext
|
156
|
+
default_content_type = ::Rack::Mime.mime_type(default_ext, "text/plain")
|
157
|
+
|
158
|
+
yield "#{path}#{default_ext}", default_content_type
|
159
|
+
yield "#{path}/#{@index}#{default_ext}", default_content_type
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def clean_path(path_info)
|
167
|
+
path = ::Rack::Utils.unescape_path path_info.chomp("/")
|
168
|
+
if ::Rack::Utils.valid_path? path
|
169
|
+
::Rack::Utils.clean_path_info path
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -1,15 +1,31 @@
|
|
1
|
+
require_relative "request_forgery_protection_fallback"
|
2
|
+
|
1
3
|
module StaticRails
|
2
4
|
class GetsCsrfToken
|
5
|
+
include RequestForgeryProtectionFallback
|
6
|
+
|
3
7
|
def call(req)
|
4
8
|
masked_authenticity_token(req.session)
|
5
9
|
end
|
6
10
|
|
7
11
|
private
|
8
12
|
|
13
|
+
def csrf_token_hmac(session, identifier)
|
14
|
+
ActionController::RequestForgeryProtection.instance_method(:csrf_token_hmac).bind(self).call(session, identifier)
|
15
|
+
end
|
16
|
+
|
17
|
+
def mask_token(raw_token)
|
18
|
+
ActionController::RequestForgeryProtection.instance_method(:mask_token).bind(self).call(raw_token)
|
19
|
+
end
|
20
|
+
|
9
21
|
def masked_authenticity_token(session, form_options: {})
|
10
22
|
ActionController::RequestForgeryProtection.instance_method(:masked_authenticity_token).bind(self).call(session, form_options)
|
11
23
|
end
|
12
24
|
|
25
|
+
def global_csrf_token(session)
|
26
|
+
ActionController::RequestForgeryProtection.instance_method(:global_csrf_token).bind(self).call(session)
|
27
|
+
end
|
28
|
+
|
13
29
|
def real_csrf_token(session)
|
14
30
|
ActionController::RequestForgeryProtection.instance_method(:real_csrf_token).bind(self).call(session)
|
15
31
|
end
|
@@ -17,7 +17,6 @@ module StaticRails
|
|
17
17
|
|
18
18
|
server_store = ServerStore.instance
|
19
19
|
server_store.ensure_all_servers_are_started
|
20
|
-
server_store.ensure_servers_are_up
|
21
20
|
|
22
21
|
req = Rack::Request.new(env)
|
23
22
|
if (req.get? || req.head?) && (site = @matches_request_to_static_site.call(req))
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module StaticRails
|
2
|
+
module RequestForgeryProtectionFallback
|
3
|
+
def method_missing(method_name, *args, **kwargs, &blk)
|
4
|
+
if respond_to?(method_name)
|
5
|
+
ActionController::RequestForgeryProtection.instance_method(method_name).bind(self).call(*args, **kwargs, &blk)
|
6
|
+
else
|
7
|
+
super
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def respond_to?(method_name, *args)
|
12
|
+
ActionController::RequestForgeryProtection.instance_method(method_name) || super
|
13
|
+
end
|
14
|
+
|
15
|
+
def respond_to_missing?(method_name, *args)
|
16
|
+
ActionController::RequestForgeryProtection.instance_method(method_name) || super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
require_relative "proxy_middleware"
|
2
2
|
require_relative "static_middleware"
|
3
3
|
require_relative "determines_whether_to_handle_request"
|
4
|
-
require_relative "gets_csrf_token"
|
5
4
|
|
6
5
|
module StaticRails
|
7
6
|
class SiteMiddleware
|
8
|
-
PATH_INFO_OBFUSCATION = "
|
7
|
+
PATH_INFO_OBFUSCATION = "__static-rails__"
|
9
8
|
|
10
9
|
def initialize(app)
|
11
10
|
@app = app
|
@@ -17,7 +16,7 @@ module StaticRails
|
|
17
16
|
def call(env)
|
18
17
|
return @app.call(env) unless @determines_whether_to_handle_request.call(env)
|
19
18
|
|
20
|
-
if require_csrf_before_processing_request?
|
19
|
+
if require_csrf_before_processing_request?
|
21
20
|
# You might be asking yourself what the hell is going on here. In short,
|
22
21
|
# This middleware sits at the top of the stack, which is too early to
|
23
22
|
# set a CSRF token in a cookie. Therefore, we've placed a subclass of
|
@@ -41,7 +40,7 @@ module StaticRails
|
|
41
40
|
#
|
42
41
|
# (By the way, this was all Matthew Draper's bright idea. You can
|
43
42
|
# compliment him here: https://github.com/matthewd )
|
44
|
-
@app.call(env.merge("PATH_INFO" => env["PATH_INFO"]
|
43
|
+
@app.call(env.merge("PATH_INFO" => PATH_INFO_OBFUSCATION + env["PATH_INFO"]))
|
45
44
|
elsif StaticRails.config.proxy_requests
|
46
45
|
@proxy_middleware.call(env)
|
47
46
|
elsif StaticRails.config.serve_compiled_assets
|
@@ -55,9 +54,5 @@ module StaticRails
|
|
55
54
|
def require_csrf_before_processing_request?
|
56
55
|
StaticRails.config.set_csrf_token_cookie
|
57
56
|
end
|
58
|
-
|
59
|
-
def csrf_token_is_set?(env)
|
60
|
-
Rack::Request.new(env).cookies.has_key?("_csrf_token")
|
61
|
-
end
|
62
57
|
end
|
63
58
|
end
|
@@ -1,30 +1,34 @@
|
|
1
1
|
require_relative "site_middleware"
|
2
2
|
require_relative "determines_whether_to_handle_request"
|
3
|
+
require_relative "validates_csrf_token"
|
3
4
|
require_relative "gets_csrf_token"
|
4
5
|
|
5
6
|
module StaticRails
|
6
7
|
class SitePlusCsrfMiddleware < SiteMiddleware
|
7
8
|
def initialize(app)
|
8
9
|
@determines_whether_to_handle_request = DeterminesWhetherToHandleRequest.new
|
10
|
+
@validates_csrf_token = ValidatesCsrfToken.new
|
9
11
|
@gets_csrf_token = GetsCsrfToken.new
|
10
12
|
super
|
11
13
|
end
|
12
14
|
|
13
15
|
def call(env)
|
14
|
-
return @app.call(env) unless @determines_whether_to_handle_request.call(env)
|
16
|
+
return @app.call(env) unless env["PATH_INFO"]&.start_with?(PATH_INFO_OBFUSCATION) || @determines_whether_to_handle_request.call(env)
|
15
17
|
|
16
18
|
env = env.merge(
|
17
|
-
"PATH_INFO" => env["PATH_INFO"].gsub(
|
19
|
+
"PATH_INFO" => env["PATH_INFO"].gsub(/^#{PATH_INFO_OBFUSCATION}/, "")
|
18
20
|
)
|
19
21
|
status, headers, body = super(env)
|
20
22
|
|
21
23
|
if StaticRails.config.set_csrf_token_cookie
|
22
24
|
req = Rack::Request.new(env)
|
23
25
|
res = Rack::Response.new(body, status, headers)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
if needs_new_csrf_token?(req)
|
27
|
+
res.set_cookie("_csrf_token", {
|
28
|
+
value: @gets_csrf_token.call(req),
|
29
|
+
path: "/"
|
30
|
+
})
|
31
|
+
end
|
28
32
|
res.finish
|
29
33
|
else
|
30
34
|
[status, headers, body]
|
@@ -36,5 +40,11 @@ module StaticRails
|
|
36
40
|
def require_csrf_before_processing_request?
|
37
41
|
false
|
38
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def needs_new_csrf_token?(req)
|
47
|
+
!req.cookies.has_key?("_csrf_token") || !@validates_csrf_token.call(req)
|
48
|
+
end
|
39
49
|
end
|
40
50
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "rack-proxy"
|
2
|
-
require "action_dispatch/middleware/static"
|
3
2
|
|
3
|
+
require_relative "file_handler"
|
4
4
|
require_relative "matches_request_to_static_site"
|
5
5
|
|
6
6
|
module StaticRails
|
@@ -18,9 +18,8 @@ module StaticRails
|
|
18
18
|
if (req.get? || req.head?) && (site = @matches_request_to_static_site.call(req))
|
19
19
|
file_handler = file_handler_for(site)
|
20
20
|
path = req.path_info.gsub(/^#{site.url_root_path}/, "").chomp("/")
|
21
|
-
if (
|
22
|
-
req
|
23
|
-
return file_handler.serve(req)
|
21
|
+
if (found = find_file_for(file_handler, site, path, req.accept_encoding))
|
22
|
+
return file_handler.serve(req, *found)
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
@@ -32,16 +31,20 @@ module StaticRails
|
|
32
31
|
# The same file handler used by Rails when serving up files from /public
|
33
32
|
# See: actionpack/lib/action_dispatch/middleware/static.rb
|
34
33
|
def file_handler_for(site)
|
35
|
-
@file_handlers[site] ||=
|
36
|
-
StaticRails.config.app.root.join(site.compile_dir).to_s
|
34
|
+
@file_handlers[site] ||= FileHandler.new(
|
35
|
+
StaticRails.config.app.root.join(site.compile_dir).to_s,
|
36
|
+
headers: {
|
37
|
+
"cache-control" => "public; max-age=31536000"
|
38
|
+
},
|
39
|
+
compressible_content_types: /^text\/|[\/+](javascript|json|text|xml|css|yaml)$/i
|
37
40
|
)
|
38
41
|
end
|
39
42
|
|
40
|
-
def
|
41
|
-
if (
|
42
|
-
|
43
|
+
def find_file_for(file_handler, site, path, accept_encoding)
|
44
|
+
if (found = file_handler.find_file(path, accept_encoding: accept_encoding))
|
45
|
+
found
|
43
46
|
elsif site.compile_404_file_path.present?
|
44
|
-
file_handler.
|
47
|
+
file_handler.find_file(site.compile_404_file_path, accept_encoding: accept_encoding)
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative "request_forgery_protection_fallback"
|
2
|
+
|
3
|
+
module StaticRails
|
4
|
+
class ValidatesCsrfToken
|
5
|
+
include RequestForgeryProtectionFallback
|
6
|
+
|
7
|
+
def call(req)
|
8
|
+
valid_authenticity_token?(req.session, req.cookies["_csrf_token"])
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
[
|
14
|
+
:compare_with_global_token,
|
15
|
+
:global_csrf_token,
|
16
|
+
:csrf_token_hmac,
|
17
|
+
:valid_authenticity_token?,
|
18
|
+
:unmask_token,
|
19
|
+
:compare_with_real_token,
|
20
|
+
:valid_per_form_csrf_token?,
|
21
|
+
:xor_byte_strings,
|
22
|
+
:real_csrf_token
|
23
|
+
].each do |method|
|
24
|
+
define_method method do |*args, **kwargs, &blk|
|
25
|
+
ActionController::RequestForgeryProtection.instance_method(method).bind(self).call(*args, **kwargs, &blk)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def per_form_csrf_tokens
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/static-rails/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: static-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -64,17 +64,20 @@ files:
|
|
64
64
|
- lib/static-rails/configuration.rb
|
65
65
|
- lib/static-rails/determines_whether_to_handle_request.rb
|
66
66
|
- lib/static-rails/error.rb
|
67
|
+
- lib/static-rails/file_handler.rb
|
67
68
|
- lib/static-rails/gets_csrf_token.rb
|
68
69
|
- lib/static-rails/matches_request_to_static_site.rb
|
69
70
|
- lib/static-rails/proxy_middleware.rb
|
70
71
|
- lib/static-rails/rack_server_check.rb
|
71
72
|
- lib/static-rails/railtie.rb
|
73
|
+
- lib/static-rails/request_forgery_protection_fallback.rb
|
72
74
|
- lib/static-rails/server.rb
|
73
75
|
- lib/static-rails/server_store.rb
|
74
76
|
- lib/static-rails/site.rb
|
75
77
|
- lib/static-rails/site_middleware.rb
|
76
78
|
- lib/static-rails/site_plus_csrf_middleware.rb
|
77
79
|
- lib/static-rails/static_middleware.rb
|
80
|
+
- lib/static-rails/validates_csrf_token.rb
|
78
81
|
- lib/static-rails/version.rb
|
79
82
|
- lib/static-rails/waits_for_connection.rb
|
80
83
|
- lib/tasks/static-rails.rake
|