actionpack 4.1.7 → 4.2.1
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 +311 -527
- data/README.rdoc +7 -2
- data/lib/abstract_controller/base.rb +16 -6
- data/lib/abstract_controller/callbacks.rb +28 -51
- data/lib/abstract_controller/helpers.rb +11 -4
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/url_for.rb +1 -1
- data/lib/action_controller/base.rb +2 -1
- data/lib/action_controller/caching/fragments.rb +7 -1
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +26 -26
- data/lib/action_controller/metal/conditional_get.rb +37 -12
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/exceptions.rb +1 -1
- data/lib/action_controller/metal/force_ssl.rb +1 -1
- data/lib/action_controller/metal/head.rb +7 -3
- data/lib/action_controller/metal/http_authentication.rb +14 -9
- data/lib/action_controller/metal/instrumentation.rb +8 -5
- data/lib/action_controller/metal/live.rb +57 -6
- data/lib/action_controller/metal/mime_responds.rb +23 -246
- data/lib/action_controller/metal/params_wrapper.rb +2 -2
- data/lib/action_controller/metal/rack_delegation.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +14 -8
- data/lib/action_controller/metal/renderers.rb +30 -10
- data/lib/action_controller/metal/rendering.rb +2 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +78 -7
- data/lib/action_controller/metal/streaming.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +125 -12
- data/lib/action_controller/metal/url_for.rb +11 -12
- data/lib/action_controller/metal.rb +12 -11
- data/lib/action_controller/model_naming.rb +1 -1
- data/lib/action_controller/railtie.rb +4 -0
- data/lib/action_controller/test_case.rb +112 -75
- data/lib/action_controller.rb +1 -1
- data/lib/action_dispatch/http/cache.rb +5 -4
- data/lib/action_dispatch/http/filter_parameters.rb +2 -2
- data/lib/action_dispatch/http/headers.rb +43 -9
- data/lib/action_dispatch/http/mime_negotiation.rb +10 -3
- data/lib/action_dispatch/http/mime_type.rb +2 -2
- data/lib/action_dispatch/http/parameter_filter.rb +1 -1
- data/lib/action_dispatch/http/parameters.rb +11 -26
- data/lib/action_dispatch/http/request.rb +37 -11
- data/lib/action_dispatch/http/response.rb +70 -18
- data/lib/action_dispatch/http/upload.rb +3 -8
- data/lib/action_dispatch/http/url.rb +88 -69
- data/lib/action_dispatch/journey/formatter.rb +33 -17
- data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +20 -28
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
- data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
- data/lib/action_dispatch/journey/nodes/node.rb +4 -0
- data/lib/action_dispatch/journey/parser.rb +52 -60
- data/lib/action_dispatch/journey/parser.y +11 -10
- data/lib/action_dispatch/journey/path/pattern.rb +16 -19
- data/lib/action_dispatch/journey/route.rb +3 -18
- data/lib/action_dispatch/journey/router/strexp.rb +9 -6
- data/lib/action_dispatch/journey/router.rb +53 -77
- data/lib/action_dispatch/journey/scanner.rb +5 -5
- data/lib/action_dispatch/journey/visitors.rb +81 -92
- data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
- data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
- data/lib/action_dispatch/middleware/callbacks.rb +1 -1
- data/lib/action_dispatch/middleware/cookies.rb +29 -29
- data/lib/action_dispatch/middleware/debug_exceptions.rb +15 -4
- data/lib/action_dispatch/middleware/exception_wrapper.rb +50 -18
- data/lib/action_dispatch/middleware/flash.rb +13 -7
- data/lib/action_dispatch/middleware/params_parser.rb +1 -1
- data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
- data/lib/action_dispatch/middleware/request_id.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
- data/lib/action_dispatch/middleware/static.rb +66 -37
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +37 -9
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
- data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +4 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +2 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +5 -12
- data/lib/action_dispatch/routing/mapper.rb +410 -281
- data/lib/action_dispatch/routing/polymorphic_routes.rb +191 -79
- data/lib/action_dispatch/routing/redirection.rb +10 -12
- data/lib/action_dispatch/routing/route_set.rb +297 -168
- data/lib/action_dispatch/routing/url_for.rb +15 -4
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
- data/lib/action_dispatch/testing/assertions/response.rb +2 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/integration.rb +24 -19
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +7 -0
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +55 -13
- data/lib/action_controller/metal/responder.rb +0 -297
|
@@ -20,62 +20,32 @@ module ActionDispatch
|
|
|
20
20
|
# :nodoc:
|
|
21
21
|
VERSION = '2.0.0'
|
|
22
22
|
|
|
23
|
-
class NullReq # :nodoc:
|
|
24
|
-
attr_reader :env
|
|
25
|
-
def initialize(env)
|
|
26
|
-
@env = env
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def request_method
|
|
30
|
-
env['REQUEST_METHOD']
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def path_info
|
|
34
|
-
env['PATH_INFO']
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def ip
|
|
38
|
-
env['REMOTE_ADDR']
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def [](k)
|
|
42
|
-
env[k]
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
attr_reader :request_class, :formatter
|
|
47
23
|
attr_accessor :routes
|
|
48
24
|
|
|
49
|
-
def initialize(routes
|
|
50
|
-
@
|
|
51
|
-
@params_key = options[:parameters_key]
|
|
52
|
-
@request_class = options[:request_class] || NullReq
|
|
53
|
-
@routes = routes
|
|
25
|
+
def initialize(routes)
|
|
26
|
+
@routes = routes
|
|
54
27
|
end
|
|
55
28
|
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
script_name
|
|
61
|
-
'PATH_INFO',
|
|
62
|
-
@params_key)
|
|
29
|
+
def serve(req)
|
|
30
|
+
find_routes(req).each do |match, parameters, route|
|
|
31
|
+
set_params = req.path_parameters
|
|
32
|
+
path_info = req.path_info
|
|
33
|
+
script_name = req.script_name
|
|
63
34
|
|
|
64
35
|
unless route.path.anchored
|
|
65
|
-
|
|
66
|
-
path_info = match.post_match
|
|
67
|
-
|
|
68
|
-
env['PATH_INFO'] = "/" + path_info unless path_info.start_with? "/"
|
|
36
|
+
req.script_name = (script_name.to_s + match.to_s).chomp('/')
|
|
37
|
+
req.path_info = match.post_match
|
|
38
|
+
req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
|
|
69
39
|
end
|
|
70
40
|
|
|
71
|
-
|
|
41
|
+
req.path_parameters = set_params.merge parameters
|
|
72
42
|
|
|
73
|
-
status, headers, body = route.app.
|
|
43
|
+
status, headers, body = route.app.serve(req)
|
|
74
44
|
|
|
75
45
|
if 'pass' == headers['X-Cascade']
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
46
|
+
req.script_name = script_name
|
|
47
|
+
req.path_info = path_info
|
|
48
|
+
req.path_parameters = set_params
|
|
79
49
|
next
|
|
80
50
|
end
|
|
81
51
|
|
|
@@ -85,14 +55,14 @@ module ActionDispatch
|
|
|
85
55
|
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
|
|
86
56
|
end
|
|
87
57
|
|
|
88
|
-
def recognize(
|
|
89
|
-
find_routes(
|
|
58
|
+
def recognize(rails_req)
|
|
59
|
+
find_routes(rails_req).each do |match, parameters, route|
|
|
90
60
|
unless route.path.anchored
|
|
91
|
-
|
|
92
|
-
|
|
61
|
+
rails_req.script_name = match.to_s
|
|
62
|
+
rails_req.path_info = match.post_match.sub(/^([^\/])/, '/\1')
|
|
93
63
|
end
|
|
94
64
|
|
|
95
|
-
yield(route,
|
|
65
|
+
yield(route, parameters)
|
|
96
66
|
end
|
|
97
67
|
end
|
|
98
68
|
|
|
@@ -123,45 +93,51 @@ module ActionDispatch
|
|
|
123
93
|
|
|
124
94
|
def filter_routes(path)
|
|
125
95
|
return [] unless ast
|
|
126
|
-
|
|
127
|
-
data ? data.memos : []
|
|
96
|
+
simulator.memos(path) { [] }
|
|
128
97
|
end
|
|
129
98
|
|
|
130
|
-
def find_routes
|
|
131
|
-
req = request_class.new(env)
|
|
132
|
-
|
|
99
|
+
def find_routes req
|
|
133
100
|
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
|
|
134
101
|
r.path.match(req.path_info)
|
|
135
102
|
}
|
|
136
|
-
routes.concat get_routes_as_head(routes)
|
|
137
103
|
|
|
138
|
-
routes
|
|
104
|
+
routes =
|
|
105
|
+
if req.request_method == "HEAD"
|
|
106
|
+
match_head_routes(routes, req)
|
|
107
|
+
else
|
|
108
|
+
match_routes(routes, req)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
routes.sort_by!(&:precedence)
|
|
139
112
|
|
|
140
113
|
routes.map! { |r|
|
|
141
114
|
match_data = r.path.match(req.path_info)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
[match_data,
|
|
115
|
+
path_parameters = r.defaults.dup
|
|
116
|
+
match_data.names.zip(match_data.captures) { |name,val|
|
|
117
|
+
path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
|
|
118
|
+
}
|
|
119
|
+
[match_data, path_parameters, r]
|
|
147
120
|
}
|
|
148
121
|
end
|
|
149
122
|
|
|
150
|
-
def
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
123
|
+
def match_head_routes(routes, req)
|
|
124
|
+
routes.delete_if { |route| route.verb == // }
|
|
125
|
+
head_routes = match_routes(routes, req)
|
|
126
|
+
|
|
127
|
+
if head_routes.empty?
|
|
128
|
+
begin
|
|
129
|
+
req.request_method = "GET"
|
|
130
|
+
match_routes(routes, req)
|
|
131
|
+
ensure
|
|
132
|
+
req.request_method = "HEAD"
|
|
133
|
+
end
|
|
134
|
+
else
|
|
135
|
+
head_routes
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def match_routes(routes, req)
|
|
140
|
+
routes.select { |r| r.matches?(req) }
|
|
165
141
|
end
|
|
166
142
|
end
|
|
167
143
|
end
|
|
@@ -39,18 +39,18 @@ module ActionDispatch
|
|
|
39
39
|
[:SLASH, text]
|
|
40
40
|
when text = @ss.scan(/\*\w+/)
|
|
41
41
|
[:STAR, text]
|
|
42
|
-
when text = @ss.scan(
|
|
42
|
+
when text = @ss.scan(/(?<!\\)\(/)
|
|
43
43
|
[:LPAREN, text]
|
|
44
|
-
when text = @ss.scan(
|
|
44
|
+
when text = @ss.scan(/(?<!\\)\)/)
|
|
45
45
|
[:RPAREN, text]
|
|
46
46
|
when text = @ss.scan(/\|/)
|
|
47
47
|
[:OR, text]
|
|
48
48
|
when text = @ss.scan(/\./)
|
|
49
49
|
[:DOT, text]
|
|
50
|
-
when text = @ss.scan(
|
|
50
|
+
when text = @ss.scan(/(?<!\\):\w+/)
|
|
51
51
|
[:SYMBOL, text]
|
|
52
|
-
when text = @ss.scan(/[\w
|
|
53
|
-
[:LITERAL, text]
|
|
52
|
+
when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\:|\\\(|\\\))+/)
|
|
53
|
+
[:LITERAL, text.tr('\\', '')]
|
|
54
54
|
# any char
|
|
55
55
|
when text = @ss.scan(/./)
|
|
56
56
|
[:LITERAL, text]
|
|
@@ -1,14 +1,57 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
|
-
require 'thread_safe'
|
|
4
|
-
|
|
5
3
|
module ActionDispatch
|
|
6
4
|
module Journey # :nodoc:
|
|
5
|
+
class Format
|
|
6
|
+
ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) }
|
|
7
|
+
ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) }
|
|
8
|
+
|
|
9
|
+
class Parameter < Struct.new(:name, :escaper)
|
|
10
|
+
def escape(value); escaper.call value; end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.required_path(symbol)
|
|
14
|
+
Parameter.new symbol, ESCAPE_PATH
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.required_segment(symbol)
|
|
18
|
+
Parameter.new symbol, ESCAPE_SEGMENT
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(parts)
|
|
22
|
+
@parts = parts
|
|
23
|
+
@children = []
|
|
24
|
+
@parameters = []
|
|
25
|
+
|
|
26
|
+
parts.each_with_index do |object,i|
|
|
27
|
+
case object
|
|
28
|
+
when Journey::Format
|
|
29
|
+
@children << i
|
|
30
|
+
when Parameter
|
|
31
|
+
@parameters << i
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def evaluate(hash)
|
|
37
|
+
parts = @parts.dup
|
|
38
|
+
|
|
39
|
+
@parameters.each do |index|
|
|
40
|
+
param = parts[index]
|
|
41
|
+
value = hash[param.name]
|
|
42
|
+
return ''.freeze unless value
|
|
43
|
+
parts[index] = param.escape value
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@children.each { |index| parts[index] = parts[index].evaluate(hash) }
|
|
47
|
+
|
|
48
|
+
parts.join
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
7
52
|
module Visitors # :nodoc:
|
|
8
53
|
class Visitor # :nodoc:
|
|
9
|
-
DISPATCH_CACHE =
|
|
10
|
-
h[k] = :"visit_#{k}"
|
|
11
|
-
}
|
|
54
|
+
DISPATCH_CACHE = {}
|
|
12
55
|
|
|
13
56
|
def accept(node)
|
|
14
57
|
visit(node)
|
|
@@ -38,11 +81,41 @@ module ActionDispatch
|
|
|
38
81
|
def visit_STAR(n); unary(n); end
|
|
39
82
|
|
|
40
83
|
def terminal(node); end
|
|
41
|
-
|
|
42
|
-
|
|
84
|
+
def visit_LITERAL(n); terminal(n); end
|
|
85
|
+
def visit_SYMBOL(n); terminal(n); end
|
|
86
|
+
def visit_SLASH(n); terminal(n); end
|
|
87
|
+
def visit_DOT(n); terminal(n); end
|
|
88
|
+
|
|
89
|
+
private_instance_methods(false).each do |pim|
|
|
90
|
+
next unless pim =~ /^visit_(.*)$/
|
|
91
|
+
DISPATCH_CACHE[$1.to_sym] = pim
|
|
43
92
|
end
|
|
44
93
|
end
|
|
45
94
|
|
|
95
|
+
class FormatBuilder < Visitor # :nodoc:
|
|
96
|
+
def accept(node); Journey::Format.new(super); end
|
|
97
|
+
def terminal(node); [node.left]; end
|
|
98
|
+
|
|
99
|
+
def binary(node)
|
|
100
|
+
visit(node.left) + visit(node.right)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def visit_GROUP(n); [Journey::Format.new(unary(n))]; end
|
|
104
|
+
|
|
105
|
+
def visit_STAR(n)
|
|
106
|
+
[Journey::Format.required_path(n.left.to_sym)]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def visit_SYMBOL(n)
|
|
110
|
+
symbol = n.to_sym
|
|
111
|
+
if symbol == :controller
|
|
112
|
+
[Journey::Format.required_path(symbol)]
|
|
113
|
+
else
|
|
114
|
+
[Journey::Format.required_segment(symbol)]
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
46
119
|
# Loop through the requirements AST
|
|
47
120
|
class Each < Visitor # :nodoc:
|
|
48
121
|
attr_reader :block
|
|
@@ -52,8 +125,8 @@ module ActionDispatch
|
|
|
52
125
|
end
|
|
53
126
|
|
|
54
127
|
def visit(node)
|
|
55
|
-
super
|
|
56
128
|
block.call(node)
|
|
129
|
+
super
|
|
57
130
|
end
|
|
58
131
|
end
|
|
59
132
|
|
|
@@ -77,90 +150,6 @@ module ActionDispatch
|
|
|
77
150
|
end
|
|
78
151
|
end
|
|
79
152
|
|
|
80
|
-
class OptimizedPath < Visitor # :nodoc:
|
|
81
|
-
def accept(node)
|
|
82
|
-
Array(visit(node))
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
private
|
|
86
|
-
|
|
87
|
-
def visit_CAT(node)
|
|
88
|
-
[visit(node.left), visit(node.right)].flatten
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def visit_SYMBOL(node)
|
|
92
|
-
node.left[1..-1].to_sym
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def visit_STAR(node)
|
|
96
|
-
visit(node.left)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def visit_GROUP(node)
|
|
100
|
-
[]
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
%w{ LITERAL SLASH DOT }.each do |t|
|
|
104
|
-
class_eval %{ def visit_#{t}(n); n.left; end }, __FILE__, __LINE__
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Used for formatting urls (url_for)
|
|
109
|
-
class Formatter < Visitor # :nodoc:
|
|
110
|
-
attr_reader :options
|
|
111
|
-
|
|
112
|
-
def initialize(options)
|
|
113
|
-
@options = options
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
private
|
|
117
|
-
def escape_path(value)
|
|
118
|
-
Router::Utils.escape_path(value)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def escape_segment(value)
|
|
122
|
-
Router::Utils.escape_segment(value)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def visit(node, optional = false)
|
|
126
|
-
case node.type
|
|
127
|
-
when :LITERAL, :SLASH, :DOT
|
|
128
|
-
node.left
|
|
129
|
-
when :STAR
|
|
130
|
-
visit_STAR(node.left)
|
|
131
|
-
when :GROUP
|
|
132
|
-
visit(node.left, true)
|
|
133
|
-
when :CAT
|
|
134
|
-
visit_CAT(node, optional)
|
|
135
|
-
when :SYMBOL
|
|
136
|
-
visit_SYMBOL(node, node.to_sym)
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def visit_CAT(node, optional)
|
|
141
|
-
left = visit(node.left, optional)
|
|
142
|
-
right = visit(node.right, optional)
|
|
143
|
-
|
|
144
|
-
if optional && !(right && left)
|
|
145
|
-
""
|
|
146
|
-
else
|
|
147
|
-
[left, right].join
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def visit_STAR(node)
|
|
152
|
-
if value = options[node.to_sym]
|
|
153
|
-
escape_path(value)
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def visit_SYMBOL(node, name)
|
|
158
|
-
if value = options[name]
|
|
159
|
-
name == :controller ? escape_path(value) : escape_segment(value)
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
153
|
class Dot < Visitor # :nodoc:
|
|
165
154
|
def initialize
|
|
166
155
|
@nodes = []
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<title><%= title %></title>
|
|
5
|
-
<link rel="stylesheet" href="https://
|
|
5
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.css" type="text/css">
|
|
6
6
|
<style>
|
|
7
7
|
<% stylesheets.each do |style| %>
|
|
8
8
|
<%= style %>
|
|
9
9
|
<% end %>
|
|
10
10
|
</style>
|
|
11
|
-
<script src="https://
|
|
11
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.min.js" type="text/javascript"></script>
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="wrapper">
|
|
@@ -3,6 +3,7 @@ require 'active_support/core_ext/module/attribute_accessors'
|
|
|
3
3
|
require 'active_support/core_ext/object/blank'
|
|
4
4
|
require 'active_support/key_generator'
|
|
5
5
|
require 'active_support/message_verifier'
|
|
6
|
+
require 'active_support/json'
|
|
6
7
|
|
|
7
8
|
module ActionDispatch
|
|
8
9
|
class Request < Rack::Request
|
|
@@ -70,11 +71,13 @@ module ActionDispatch
|
|
|
70
71
|
# restrict to the domain level. If you use a schema like www.example.com
|
|
71
72
|
# and want to share session with user.example.com set <tt>:domain</tt>
|
|
72
73
|
# to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
|
|
73
|
-
# <tt>:all</tt> again when deleting cookies.
|
|
74
|
+
# <tt>:all</tt> or <tt>Array</tt> again when deleting cookies.
|
|
74
75
|
#
|
|
75
76
|
# domain: nil # Does not sets cookie domain. (default)
|
|
76
77
|
# domain: :all # Allow the cookie for the top most level
|
|
77
|
-
#
|
|
78
|
+
# # domain and subdomains.
|
|
79
|
+
# domain: %w(.example.com .example.org) # Allow the cookie
|
|
80
|
+
# # for concrete domain names.
|
|
78
81
|
#
|
|
79
82
|
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
|
|
80
83
|
# * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
|
|
@@ -90,6 +93,7 @@ module ActionDispatch
|
|
|
90
93
|
SECRET_TOKEN = "action_dispatch.secret_token".freeze
|
|
91
94
|
SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
|
|
92
95
|
COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
|
|
96
|
+
COOKIES_DIGEST = "action_dispatch.cookies_digest".freeze
|
|
93
97
|
|
|
94
98
|
# Cookies can typically store 4096 bytes.
|
|
95
99
|
MAX_COOKIE_SIZE = 4096
|
|
@@ -118,7 +122,7 @@ module ActionDispatch
|
|
|
118
122
|
# the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
|
|
119
123
|
# cookie was tampered with by the user (or a 3rd party), nil will be returned.
|
|
120
124
|
#
|
|
121
|
-
# If +secrets.secret_key_base+ and +
|
|
125
|
+
# If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
|
|
122
126
|
# legacy cookies signed with the old key generator will be transparently upgraded.
|
|
123
127
|
#
|
|
124
128
|
# This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
|
|
@@ -141,7 +145,7 @@ module ActionDispatch
|
|
|
141
145
|
# Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
|
|
142
146
|
# If the cookie was tampered with by the user (or a 3rd party), nil will be returned.
|
|
143
147
|
#
|
|
144
|
-
# If +secrets.secret_key_base+ and +
|
|
148
|
+
# If +secrets.secret_key_base+ and +secrets.secret_token+ (deprecated) are both set,
|
|
145
149
|
# legacy cookies signed with the old key generator will be transparently upgraded.
|
|
146
150
|
#
|
|
147
151
|
# This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
|
|
@@ -173,10 +177,14 @@ module ActionDispatch
|
|
|
173
177
|
end
|
|
174
178
|
end
|
|
175
179
|
|
|
180
|
+
# Passing the ActiveSupport::MessageEncryptor::NullSerializer downstream
|
|
181
|
+
# to the Message{Encryptor,Verifier} allows us to handle the
|
|
182
|
+
# (de)serialization step within the cookie jar, which gives us the
|
|
183
|
+
# opportunity to detect and migrate legacy cookies.
|
|
176
184
|
module VerifyAndUpgradeLegacySignedMessage
|
|
177
185
|
def initialize(*args)
|
|
178
186
|
super
|
|
179
|
-
@legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: NullSerializer)
|
|
187
|
+
@legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
|
180
188
|
end
|
|
181
189
|
|
|
182
190
|
def verify_and_upgrade_legacy_signed_message(name, signed_message)
|
|
@@ -212,7 +220,8 @@ module ActionDispatch
|
|
|
212
220
|
secret_token: env[SECRET_TOKEN],
|
|
213
221
|
secret_key_base: env[SECRET_KEY_BASE],
|
|
214
222
|
upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
|
|
215
|
-
serializer: env[COOKIES_SERIALIZER]
|
|
223
|
+
serializer: env[COOKIES_SERIALIZER],
|
|
224
|
+
digest: env[COOKIES_DIGEST]
|
|
216
225
|
}
|
|
217
226
|
end
|
|
218
227
|
|
|
@@ -289,8 +298,8 @@ module ActionDispatch
|
|
|
289
298
|
end
|
|
290
299
|
end
|
|
291
300
|
|
|
292
|
-
# Sets the cookie named +name+. The second argument may be the
|
|
293
|
-
# value
|
|
301
|
+
# Sets the cookie named +name+. The second argument may be the cookie's
|
|
302
|
+
# value or a hash of options as documented above.
|
|
294
303
|
def []=(name, options)
|
|
295
304
|
if options.is_a?(Hash)
|
|
296
305
|
options.symbolize_keys!
|
|
@@ -385,24 +394,11 @@ module ActionDispatch
|
|
|
385
394
|
|
|
386
395
|
class JsonSerializer
|
|
387
396
|
def self.load(value)
|
|
388
|
-
JSON.
|
|
397
|
+
ActiveSupport::JSON.decode(value)
|
|
389
398
|
end
|
|
390
399
|
|
|
391
400
|
def self.dump(value)
|
|
392
|
-
JSON.
|
|
393
|
-
end
|
|
394
|
-
end
|
|
395
|
-
|
|
396
|
-
# Passing the NullSerializer downstream to the Message{Encryptor,Verifier}
|
|
397
|
-
# allows us to handle the (de)serialization step within the cookie jar,
|
|
398
|
-
# which gives us the opportunity to detect and migrate legacy cookies.
|
|
399
|
-
class NullSerializer
|
|
400
|
-
def self.load(value)
|
|
401
|
-
value
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
def self.dump(value)
|
|
405
|
-
value
|
|
401
|
+
ActiveSupport::JSON.encode(value)
|
|
406
402
|
end
|
|
407
403
|
end
|
|
408
404
|
|
|
@@ -441,6 +437,10 @@ module ActionDispatch
|
|
|
441
437
|
serializer
|
|
442
438
|
end
|
|
443
439
|
end
|
|
440
|
+
|
|
441
|
+
def digest
|
|
442
|
+
@options[:digest] || 'SHA1'
|
|
443
|
+
end
|
|
444
444
|
end
|
|
445
445
|
|
|
446
446
|
class SignedCookieJar #:nodoc:
|
|
@@ -451,7 +451,7 @@ module ActionDispatch
|
|
|
451
451
|
@parent_jar = parent_jar
|
|
452
452
|
@options = options
|
|
453
453
|
secret = key_generator.generate_key(@options[:signed_cookie_salt])
|
|
454
|
-
@verifier = ActiveSupport::MessageVerifier.new(secret, serializer: NullSerializer)
|
|
454
|
+
@verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
|
455
455
|
end
|
|
456
456
|
|
|
457
457
|
def [](name)
|
|
@@ -468,7 +468,7 @@ module ActionDispatch
|
|
|
468
468
|
options = { :value => @verifier.generate(serialize(name, options)) }
|
|
469
469
|
end
|
|
470
470
|
|
|
471
|
-
raise CookieOverflow if options[:value].
|
|
471
|
+
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
|
|
472
472
|
@parent_jar[name] = options
|
|
473
473
|
end
|
|
474
474
|
|
|
@@ -481,7 +481,7 @@ module ActionDispatch
|
|
|
481
481
|
end
|
|
482
482
|
|
|
483
483
|
# UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if
|
|
484
|
-
#
|
|
484
|
+
# secrets.secret_token and secrets.secret_key_base are both set. It reads
|
|
485
485
|
# legacy cookies signed with the old dummy key generator and re-saves
|
|
486
486
|
# them using the new key generator to provide a smooth upgrade path.
|
|
487
487
|
class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
|
|
@@ -508,7 +508,7 @@ module ActionDispatch
|
|
|
508
508
|
@options = options
|
|
509
509
|
secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
|
|
510
510
|
sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
|
|
511
|
-
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: NullSerializer)
|
|
511
|
+
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer)
|
|
512
512
|
end
|
|
513
513
|
|
|
514
514
|
def [](name)
|
|
@@ -526,7 +526,7 @@ module ActionDispatch
|
|
|
526
526
|
|
|
527
527
|
options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value]))
|
|
528
528
|
|
|
529
|
-
raise CookieOverflow if options[:value].
|
|
529
|
+
raise CookieOverflow if options[:value].bytesize > MAX_COOKIE_SIZE
|
|
530
530
|
@parent_jar[name] = options
|
|
531
531
|
end
|
|
532
532
|
|
|
@@ -539,7 +539,7 @@ module ActionDispatch
|
|
|
539
539
|
end
|
|
540
540
|
|
|
541
541
|
# UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore
|
|
542
|
-
# instead of EncryptedCookieJar if
|
|
542
|
+
# instead of EncryptedCookieJar if secrets.secret_token and secrets.secret_key_base
|
|
543
543
|
# are both set. It reads legacy cookies signed with the old dummy key generator and
|
|
544
544
|
# encrypts and re-saves them using the new key generator to provide a smooth upgrade path.
|
|
545
545
|
class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc:
|