rack 1.6.13 → 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +694 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +157 -163
- data/Rakefile +38 -32
- data/{SPEC → SPEC.rdoc} +41 -13
- data/bin/rackup +1 -0
- data/contrib/rack_logo.svg +164 -111
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +6 -2
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +5 -4
- data/lib/rack/auth/digest/request.rb +6 -4
- data/lib/rack/body_proxy.rb +21 -15
- data/lib/rack/builder.rb +119 -26
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +70 -22
- data/lib/rack/common_logger.rb +80 -0
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +9 -8
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +60 -70
- data/lib/rack/directory.rb +117 -85
- data/lib/rack/etag.rb +9 -7
- data/lib/rack/events.rb +153 -0
- data/lib/rack/file.rb +4 -149
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +17 -19
- data/lib/rack/handler/fastcgi.rb +17 -18
- data/lib/rack/handler/lsws.rb +14 -14
- data/lib/rack/handler/scgi.rb +22 -21
- data/lib/rack/handler/thin.rb +6 -3
- data/lib/rack/handler/webrick.rb +39 -32
- data/lib/rack/handler.rb +9 -26
- data/lib/rack/head.rb +16 -18
- data/lib/rack/lint.rb +110 -64
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +17 -11
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
- data/lib/rack/mime.rb +27 -6
- data/lib/rack/mock.rb +124 -65
- data/lib/rack/multipart/generator.rb +20 -16
- data/lib/rack/multipart/parser.rb +273 -162
- data/lib/rack/multipart/uploaded_file.rb +15 -8
- data/lib/rack/multipart.rb +39 -8
- data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
- data/lib/rack/query_parser.rb +217 -0
- data/lib/rack/recursive.rb +11 -9
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +553 -305
- data/lib/rack/response.rb +244 -88
- data/lib/rack/rewindable_input.rb +5 -15
- data/lib/rack/runtime.rb +12 -18
- data/lib/rack/sendfile.rb +17 -15
- data/lib/rack/server.rb +125 -47
- data/lib/rack/session/abstract/id.rb +141 -93
- data/lib/rack/session/cookie.rb +35 -29
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +13 -11
- data/lib/rack/show_exceptions.rb +390 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +12 -12
- data/lib/rack/static.rb +48 -11
- data/lib/rack/tempfile_reaper.rb +3 -3
- data/lib/rack/urlmap.rb +26 -19
- data/lib/rack/utils.rb +212 -294
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +76 -33
- data/rack.gemspec +43 -30
- metadata +65 -187
- data/HISTORY.md +0 -375
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/commonlogger.rb +0 -72
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -223
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -101
- data/test/spec_commonlogger.rb +0 -93
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -85
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -339
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -107
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -221
- data/test/spec_handler.rb +0 -72
- data/test/spec_head.rb +0 -45
- data/test/spec_lint.rb +0 -550
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -164
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -111
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -297
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -600
- data/test/spec_nulllogger.rb +0 -20
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -1232
- data/test/spec_response.rb +0 -407
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -130
- data/test/spec_server.rb +0 -167
- data/test/spec_session_abstract_id.rb +0 -53
- data/test/spec_session_cookie.rb +0 -410
- data/test/spec_session_memcache.rb +0 -358
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -246
- data/test/spec_showexceptions.rb +0 -98
- data/test/spec_showstatus.rb +0 -103
- data/test/spec_static.rb +0 -145
- data/test/spec_tempfile_reaper.rb +0 -63
- data/test/spec_thin.rb +0 -91
- data/test/spec_urlmap.rb +0 -236
- data/test/spec_utils.rb +0 -647
- data/test/spec_version.rb +0 -17
- data/test/spec_webrick.rb +0 -184
- data/test/static/another/index.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,15 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
4
|
# bugrep: Andreas Zehnder
|
3
5
|
|
6
|
+
require_relative '../../../rack'
|
4
7
|
require 'time'
|
5
|
-
require '
|
6
|
-
require '
|
7
|
-
begin
|
8
|
-
require 'securerandom'
|
9
|
-
rescue LoadError
|
10
|
-
# We just won't get securerandom
|
11
|
-
end
|
12
|
-
require "digest/sha2"
|
8
|
+
require 'securerandom'
|
9
|
+
require 'digest/sha2'
|
13
10
|
|
14
11
|
module Rack
|
15
12
|
|
@@ -42,40 +39,39 @@ module Rack
|
|
42
39
|
end
|
43
40
|
|
44
41
|
module Abstract
|
45
|
-
ENV_SESSION_KEY = 'rack.session'.freeze
|
46
|
-
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
47
|
-
|
48
42
|
# SessionHash is responsible to lazily load the session from store.
|
49
43
|
|
50
44
|
class SessionHash
|
51
45
|
include Enumerable
|
52
46
|
attr_writer :id
|
53
47
|
|
54
|
-
|
55
|
-
|
48
|
+
Unspecified = Object.new
|
49
|
+
|
50
|
+
def self.find(req)
|
51
|
+
req.get_header RACK_SESSION
|
56
52
|
end
|
57
53
|
|
58
|
-
def self.set(
|
59
|
-
|
54
|
+
def self.set(req, session)
|
55
|
+
req.set_header RACK_SESSION, session
|
60
56
|
end
|
61
57
|
|
62
|
-
def self.set_options(
|
63
|
-
|
58
|
+
def self.set_options(req, options)
|
59
|
+
req.set_header RACK_SESSION_OPTIONS, options.dup
|
64
60
|
end
|
65
61
|
|
66
|
-
def initialize(store,
|
62
|
+
def initialize(store, req)
|
67
63
|
@store = store
|
68
|
-
@
|
64
|
+
@req = req
|
69
65
|
@loaded = false
|
70
66
|
end
|
71
67
|
|
72
68
|
def id
|
73
69
|
return @id if @loaded or instance_variable_defined?(:@id)
|
74
|
-
@id = @store.send(:extract_session_id, @
|
70
|
+
@id = @store.send(:extract_session_id, @req)
|
75
71
|
end
|
76
72
|
|
77
73
|
def options
|
78
|
-
@
|
74
|
+
@req.session_options
|
79
75
|
end
|
80
76
|
|
81
77
|
def each(&block)
|
@@ -87,7 +83,20 @@ module Rack
|
|
87
83
|
load_for_read!
|
88
84
|
@data[key.to_s]
|
89
85
|
end
|
90
|
-
|
86
|
+
|
87
|
+
def dig(key, *keys)
|
88
|
+
load_for_read!
|
89
|
+
@data.dig(key.to_s, *keys)
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch(key, default = Unspecified, &block)
|
93
|
+
load_for_read!
|
94
|
+
if default == Unspecified
|
95
|
+
@data.fetch(key.to_s, &block)
|
96
|
+
else
|
97
|
+
@data.fetch(key.to_s, default, &block)
|
98
|
+
end
|
99
|
+
end
|
91
100
|
|
92
101
|
def has_key?(key)
|
93
102
|
load_for_read!
|
@@ -109,7 +118,7 @@ module Rack
|
|
109
118
|
|
110
119
|
def destroy
|
111
120
|
clear
|
112
|
-
@id = @store.send(:
|
121
|
+
@id = @store.send(:delete_session, @req, id, options)
|
113
122
|
end
|
114
123
|
|
115
124
|
def to_hash
|
@@ -144,7 +153,7 @@ module Rack
|
|
144
153
|
def exists?
|
145
154
|
return @exists if instance_variable_defined?(:@exists)
|
146
155
|
@data = {}
|
147
|
-
@exists = @store.send(:session_exists?, @
|
156
|
+
@exists = @store.send(:session_exists?, @req)
|
148
157
|
end
|
149
158
|
|
150
159
|
def loaded?
|
@@ -157,10 +166,12 @@ module Rack
|
|
157
166
|
end
|
158
167
|
|
159
168
|
def keys
|
169
|
+
load_for_read!
|
160
170
|
@data.keys
|
161
171
|
end
|
162
172
|
|
163
173
|
def values
|
174
|
+
load_for_read!
|
164
175
|
@data.values
|
165
176
|
end
|
166
177
|
|
@@ -175,14 +186,15 @@ module Rack
|
|
175
186
|
end
|
176
187
|
|
177
188
|
def load!
|
178
|
-
@id, session = @store.send(:load_session, @
|
189
|
+
@id, session = @store.send(:load_session, @req)
|
179
190
|
@data = stringify_keys(session)
|
180
191
|
@loaded = true
|
181
192
|
end
|
182
193
|
|
183
194
|
def stringify_keys(other)
|
195
|
+
# Use transform_keys after dropping Ruby 2.4 support
|
184
196
|
hash = {}
|
185
|
-
other.each do |key, value|
|
197
|
+
other.to_hash.each do |key, value|
|
186
198
|
hash[key.to_s] = value
|
187
199
|
end
|
188
200
|
hash
|
@@ -191,14 +203,14 @@ module Rack
|
|
191
203
|
|
192
204
|
# ID sets up a basic framework for implementing an id based sessioning
|
193
205
|
# service. Cookies sent to the client for maintaining sessions will only
|
194
|
-
# contain an id reference. Only #
|
195
|
-
# required to be overwritten.
|
206
|
+
# contain an id reference. Only #find_session, #write_session and
|
207
|
+
# #delete_session are required to be overwritten.
|
196
208
|
#
|
197
209
|
# All parameters are optional.
|
198
210
|
# * :key determines the name of the cookie, by default it is
|
199
211
|
# 'rack.session'
|
200
212
|
# * :path, :domain, :expire_after, :secure, and :httponly set the related
|
201
|
-
# cookie options as by Rack::Response#
|
213
|
+
# cookie options as by Rack::Response#set_cookie
|
202
214
|
# * :skip will not a set a cookie in the response nor update the session state
|
203
215
|
# * :defer will not set a cookie in the response but still update the session
|
204
216
|
# state if it is used with a backend
|
@@ -209,9 +221,9 @@ module Rack
|
|
209
221
|
# id will be.
|
210
222
|
#
|
211
223
|
# These options can be set on a per request basis, at the location of
|
212
|
-
# env['rack.session.options']
|
213
|
-
# found within the options hash at the key :id. It is
|
214
|
-
# recommended to change its value.
|
224
|
+
# <tt>env['rack.session.options']</tt>. Additionally the id of the
|
225
|
+
# session can be found within the options hash at the key :id. It is
|
226
|
+
# highly not recommended to change its value.
|
215
227
|
#
|
216
228
|
# Is Rack::Utils::Context compatible.
|
217
229
|
#
|
@@ -220,26 +232,27 @@ module Rack
|
|
220
232
|
|
221
233
|
class Persisted
|
222
234
|
DEFAULT_OPTIONS = {
|
223
|
-
:
|
224
|
-
:
|
225
|
-
:
|
226
|
-
:
|
227
|
-
:
|
228
|
-
:
|
229
|
-
:
|
230
|
-
:
|
231
|
-
:
|
232
|
-
:
|
233
|
-
:
|
234
|
-
}
|
235
|
-
|
236
|
-
attr_reader :key, :default_options
|
237
|
-
|
238
|
-
def initialize(app, options={})
|
235
|
+
key: RACK_SESSION,
|
236
|
+
path: '/',
|
237
|
+
domain: nil,
|
238
|
+
expire_after: nil,
|
239
|
+
secure: false,
|
240
|
+
httponly: true,
|
241
|
+
defer: false,
|
242
|
+
renew: false,
|
243
|
+
sidbits: 128,
|
244
|
+
cookie_only: true,
|
245
|
+
secure_random: ::SecureRandom
|
246
|
+
}.freeze
|
247
|
+
|
248
|
+
attr_reader :key, :default_options, :sid_secure
|
249
|
+
|
250
|
+
def initialize(app, options = {})
|
239
251
|
@app = app
|
240
252
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
241
253
|
@key = @default_options.delete(:key)
|
242
254
|
@cookie_only = @default_options.delete(:cookie_only)
|
255
|
+
@same_site = @default_options.delete(:same_site)
|
243
256
|
initialize_sid
|
244
257
|
end
|
245
258
|
|
@@ -247,14 +260,21 @@ module Rack
|
|
247
260
|
context(env)
|
248
261
|
end
|
249
262
|
|
250
|
-
def context(env, app
|
251
|
-
|
252
|
-
|
253
|
-
|
263
|
+
def context(env, app = @app)
|
264
|
+
req = make_request env
|
265
|
+
prepare_session(req)
|
266
|
+
status, headers, body = app.call(req.env)
|
267
|
+
res = Rack::Response::Raw.new status, headers
|
268
|
+
commit_session(req, res)
|
269
|
+
[status, headers, body]
|
254
270
|
end
|
255
271
|
|
256
272
|
private
|
257
273
|
|
274
|
+
def make_request(env)
|
275
|
+
Rack::Request.new env
|
276
|
+
end
|
277
|
+
|
258
278
|
def initialize_sid
|
259
279
|
@sidbits = @default_options[:sidbits]
|
260
280
|
@sid_secure = @default_options[:secure_random]
|
@@ -278,26 +298,26 @@ module Rack
|
|
278
298
|
# Sets the lazy session at 'rack.session' and places options and session
|
279
299
|
# metadata into 'rack.session.options'.
|
280
300
|
|
281
|
-
def prepare_session(
|
282
|
-
session_was
|
283
|
-
|
284
|
-
|
285
|
-
|
301
|
+
def prepare_session(req)
|
302
|
+
session_was = req.get_header RACK_SESSION
|
303
|
+
session = session_class.new(self, req)
|
304
|
+
req.set_header RACK_SESSION, session
|
305
|
+
req.set_header RACK_SESSION_OPTIONS, @default_options.dup
|
306
|
+
session.merge! session_was if session_was
|
286
307
|
end
|
287
308
|
|
288
309
|
# Extracts the session id from provided cookies and passes it and the
|
289
|
-
# environment to #
|
310
|
+
# environment to #find_session.
|
290
311
|
|
291
|
-
def load_session(
|
292
|
-
sid = current_session_id(
|
293
|
-
sid, session =
|
312
|
+
def load_session(req)
|
313
|
+
sid = current_session_id(req)
|
314
|
+
sid, session = find_session(req, sid)
|
294
315
|
[sid, session || {}]
|
295
316
|
end
|
296
317
|
|
297
318
|
# Extract session id from request object.
|
298
319
|
|
299
|
-
def extract_session_id(
|
300
|
-
request = Rack::Request.new(env)
|
320
|
+
def extract_session_id(request)
|
301
321
|
sid = request.cookies[@key]
|
302
322
|
sid ||= request.params[@key] unless @cookie_only
|
303
323
|
sid
|
@@ -305,26 +325,26 @@ module Rack
|
|
305
325
|
|
306
326
|
# Returns the current session id from the SessionHash.
|
307
327
|
|
308
|
-
def current_session_id(
|
309
|
-
|
328
|
+
def current_session_id(req)
|
329
|
+
req.get_header(RACK_SESSION).id
|
310
330
|
end
|
311
331
|
|
312
332
|
# Check if the session exists or not.
|
313
333
|
|
314
|
-
def session_exists?(
|
315
|
-
value = current_session_id(
|
334
|
+
def session_exists?(req)
|
335
|
+
value = current_session_id(req)
|
316
336
|
value && !value.empty?
|
317
337
|
end
|
318
338
|
|
319
339
|
# Session should be committed if it was loaded, any of specific options like :renew, :drop
|
320
340
|
# or :expire_after was given and the security permissions match. Skips if skip is given.
|
321
341
|
|
322
|
-
def commit_session?(
|
342
|
+
def commit_session?(req, session, options)
|
323
343
|
if options[:skip]
|
324
344
|
false
|
325
345
|
else
|
326
346
|
has_session = loaded_session?(session) || forced_session_update?(session, options)
|
327
|
-
has_session && security_matches?(
|
347
|
+
has_session && security_matches?(req, options)
|
328
348
|
end
|
329
349
|
end
|
330
350
|
|
@@ -340,46 +360,50 @@ module Rack
|
|
340
360
|
options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
|
341
361
|
end
|
342
362
|
|
343
|
-
def security_matches?(
|
363
|
+
def security_matches?(request, options)
|
344
364
|
return true unless options[:secure]
|
345
|
-
request = Rack::Request.new(env)
|
346
365
|
request.ssl?
|
347
366
|
end
|
348
367
|
|
349
368
|
# Acquires the session from the environment and the session id from
|
350
|
-
# the session options and passes them to #
|
369
|
+
# the session options and passes them to #write_session. If successful
|
351
370
|
# and the :defer option is not true, a cookie will be added to the
|
352
371
|
# response with the session's id.
|
353
372
|
|
354
|
-
def commit_session(
|
355
|
-
session =
|
373
|
+
def commit_session(req, res)
|
374
|
+
session = req.get_header RACK_SESSION
|
356
375
|
options = session.options
|
357
376
|
|
358
377
|
if options[:drop] || options[:renew]
|
359
|
-
session_id =
|
360
|
-
return
|
378
|
+
session_id = delete_session(req, session.id || generate_sid, options)
|
379
|
+
return unless session_id
|
361
380
|
end
|
362
381
|
|
363
|
-
return
|
382
|
+
return unless commit_session?(req, session, options)
|
364
383
|
|
365
384
|
session.send(:load!) unless loaded_session?(session)
|
366
385
|
session_id ||= session.id
|
367
|
-
session_data = session.to_hash.delete_if { |k,v| v.nil? }
|
386
|
+
session_data = session.to_hash.delete_if { |k, v| v.nil? }
|
368
387
|
|
369
|
-
if not data =
|
370
|
-
|
388
|
+
if not data = write_session(req, session_id, session_data, options)
|
389
|
+
req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
371
390
|
elsif options[:defer] and not options[:renew]
|
372
|
-
|
391
|
+
req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
|
373
392
|
else
|
374
393
|
cookie = Hash.new
|
375
394
|
cookie[:value] = cookie_value(data)
|
376
395
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
377
396
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
378
|
-
set_cookie(env, headers, cookie.merge!(options))
|
379
|
-
end
|
380
397
|
|
381
|
-
|
398
|
+
if @same_site.respond_to? :call
|
399
|
+
cookie[:same_site] = @same_site.call(req, res)
|
400
|
+
else
|
401
|
+
cookie[:same_site] = @same_site
|
402
|
+
end
|
403
|
+
set_cookie(req, res, cookie.merge!(options))
|
404
|
+
end
|
382
405
|
end
|
406
|
+
public :commit_session
|
383
407
|
|
384
408
|
def cookie_value(data)
|
385
409
|
data
|
@@ -388,10 +412,10 @@ module Rack
|
|
388
412
|
# Sets the cookie back to the client with session id. We skip the cookie
|
389
413
|
# setting if the value didn't change (sid is the same) or expires was given.
|
390
414
|
|
391
|
-
def set_cookie(
|
392
|
-
request = Rack::Request.new(env)
|
415
|
+
def set_cookie(request, res, cookie)
|
393
416
|
if request.cookies[@key] != cookie[:value] || cookie[:expires]
|
394
|
-
|
417
|
+
res.set_cookie_header =
|
418
|
+
Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
|
395
419
|
end
|
396
420
|
end
|
397
421
|
|
@@ -406,23 +430,23 @@ module Rack
|
|
406
430
|
# If nil is provided as the session id, generation of a new valid id
|
407
431
|
# should occur within.
|
408
432
|
|
409
|
-
def
|
410
|
-
raise '#
|
433
|
+
def find_session(env, sid)
|
434
|
+
raise '#find_session not implemented.'
|
411
435
|
end
|
412
436
|
|
413
437
|
# All thread safety and session storage procedures should occur here.
|
414
438
|
# Must return the session id if the session was saved successfully, or
|
415
439
|
# false if the session could not be saved.
|
416
440
|
|
417
|
-
def
|
418
|
-
raise '#
|
441
|
+
def write_session(req, sid, session, options)
|
442
|
+
raise '#write_session not implemented.'
|
419
443
|
end
|
420
444
|
|
421
445
|
# All thread safety and session destroy procedures should occur here.
|
422
446
|
# Should return a new session id or nil if options[:drop]
|
423
447
|
|
424
|
-
def
|
425
|
-
raise '#
|
448
|
+
def delete_session(req, sid, options)
|
449
|
+
raise '#delete_session not implemented'
|
426
450
|
end
|
427
451
|
end
|
428
452
|
|
@@ -469,6 +493,30 @@ module Rack
|
|
469
493
|
end
|
470
494
|
super
|
471
495
|
end
|
496
|
+
|
497
|
+
# All thread safety and session retrieval procedures should occur here.
|
498
|
+
# Should return [session_id, session].
|
499
|
+
# If nil is provided as the session id, generation of a new valid id
|
500
|
+
# should occur within.
|
501
|
+
|
502
|
+
def find_session(req, sid)
|
503
|
+
get_session req.env, sid
|
504
|
+
end
|
505
|
+
|
506
|
+
# All thread safety and session storage procedures should occur here.
|
507
|
+
# Must return the session id if the session was saved successfully, or
|
508
|
+
# false if the session could not be saved.
|
509
|
+
|
510
|
+
def write_session(req, sid, session, options)
|
511
|
+
set_session req.env, sid, session, options
|
512
|
+
end
|
513
|
+
|
514
|
+
# All thread safety and session destroy procedures should occur here.
|
515
|
+
# Should return a new session id or nil if options[:drop]
|
516
|
+
|
517
|
+
def delete_session(req, sid, options)
|
518
|
+
destroy_session req.env, sid, options
|
519
|
+
end
|
472
520
|
end
|
473
521
|
end
|
474
522
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'zlib'
|
3
|
-
|
4
|
-
require '
|
5
|
-
require '
|
5
|
+
require_relative 'abstract/id'
|
6
|
+
require 'json'
|
7
|
+
require 'base64'
|
6
8
|
|
7
9
|
module Rack
|
8
10
|
|
@@ -48,11 +50,11 @@ module Rack
|
|
48
50
|
# Encode session cookies as Base64
|
49
51
|
class Base64
|
50
52
|
def encode(str)
|
51
|
-
|
53
|
+
::Base64.strict_encode64(str)
|
52
54
|
end
|
53
55
|
|
54
56
|
def decode(str)
|
55
|
-
|
57
|
+
::Base64.decode64(str)
|
56
58
|
end
|
57
59
|
|
58
60
|
# Encode session cookies as Marshaled Base64 data
|
@@ -71,23 +73,23 @@ module Rack
|
|
71
73
|
# valid JSON composite type, either a Hash or an Array.
|
72
74
|
class JSON < Base64
|
73
75
|
def encode(obj)
|
74
|
-
super(::
|
76
|
+
super(::JSON.dump(obj))
|
75
77
|
end
|
76
78
|
|
77
79
|
def decode(str)
|
78
80
|
return unless str
|
79
|
-
::
|
81
|
+
::JSON.parse(super(str)) rescue nil
|
80
82
|
end
|
81
83
|
end
|
82
84
|
|
83
85
|
class ZipJSON < Base64
|
84
86
|
def encode(obj)
|
85
|
-
super(Zlib::Deflate.deflate(::
|
87
|
+
super(Zlib::Deflate.deflate(::JSON.dump(obj)))
|
86
88
|
end
|
87
89
|
|
88
90
|
def decode(str)
|
89
91
|
return unless str
|
90
|
-
::
|
92
|
+
::JSON.parse(Zlib::Inflate.inflate(super(str)))
|
91
93
|
rescue
|
92
94
|
nil
|
93
95
|
end
|
@@ -102,9 +104,11 @@ module Rack
|
|
102
104
|
|
103
105
|
attr_reader :coder
|
104
106
|
|
105
|
-
def initialize(app, options={})
|
107
|
+
def initialize(app, options = {})
|
106
108
|
@secrets = options.values_at(:secret, :old_secret).compact
|
107
|
-
|
109
|
+
@hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
|
110
|
+
|
111
|
+
warn <<-MSG unless secure?(options)
|
108
112
|
SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
|
109
113
|
This poses a security threat. It is strongly recommended that you
|
110
114
|
provide a secret to prevent exploits that may be possible from crafted
|
@@ -113,39 +117,36 @@ module Rack
|
|
113
117
|
|
114
118
|
Called from: #{caller[0]}.
|
115
119
|
MSG
|
116
|
-
@coder
|
117
|
-
super(app, options.merge!(:
|
120
|
+
@coder = options[:coder] ||= Base64::Marshal.new
|
121
|
+
super(app, options.merge!(cookie_only: true))
|
118
122
|
end
|
119
123
|
|
120
124
|
private
|
121
125
|
|
122
|
-
def
|
123
|
-
data = unpacked_cookie_data(
|
126
|
+
def find_session(req, sid)
|
127
|
+
data = unpacked_cookie_data(req)
|
124
128
|
data = persistent_session_id!(data)
|
125
129
|
[data["session_id"], data]
|
126
130
|
end
|
127
131
|
|
128
|
-
def extract_session_id(
|
129
|
-
unpacked_cookie_data(
|
132
|
+
def extract_session_id(request)
|
133
|
+
unpacked_cookie_data(request)["session_id"]
|
130
134
|
end
|
131
135
|
|
132
|
-
def unpacked_cookie_data(
|
133
|
-
|
134
|
-
request = Rack::Request.new(env)
|
136
|
+
def unpacked_cookie_data(request)
|
137
|
+
request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
|
135
138
|
session_data = request.cookies[@key]
|
136
139
|
|
137
140
|
if @secrets.size > 0 && session_data
|
138
|
-
|
139
|
-
digest.reverse! if digest
|
140
|
-
session_data.reverse! if session_data
|
141
|
+
session_data, _, digest = session_data.rpartition('--')
|
141
142
|
session_data = nil unless digest_match?(session_data, digest)
|
142
143
|
end
|
143
144
|
|
144
|
-
coder.decode(session_data) || {}
|
145
|
+
request.set_header(k, coder.decode(session_data) || {})
|
145
146
|
end
|
146
147
|
end
|
147
148
|
|
148
|
-
def persistent_session_id!(data, sid=nil)
|
149
|
+
def persistent_session_id!(data, sid = nil)
|
149
150
|
data ||= {}
|
150
151
|
data["session_id"] ||= sid || generate_sid
|
151
152
|
data
|
@@ -160,7 +161,7 @@ module Rack
|
|
160
161
|
end
|
161
162
|
end
|
162
163
|
|
163
|
-
def
|
164
|
+
def write_session(req, session_id, session, options)
|
164
165
|
session = session.merge("session_id" => session_id)
|
165
166
|
session_data = coder.encode(session)
|
166
167
|
|
@@ -169,14 +170,14 @@ module Rack
|
|
169
170
|
end
|
170
171
|
|
171
172
|
if session_data.size > (4096 - @key.size)
|
172
|
-
|
173
|
+
req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
173
174
|
nil
|
174
175
|
else
|
175
176
|
SessionId.new(session_id, session_data)
|
176
177
|
end
|
177
178
|
end
|
178
179
|
|
179
|
-
def
|
180
|
+
def delete_session(req, session_id, options)
|
180
181
|
# Nothing to do here, data is in the client
|
181
182
|
generate_sid unless options[:drop]
|
182
183
|
end
|
@@ -189,7 +190,12 @@ module Rack
|
|
189
190
|
end
|
190
191
|
|
191
192
|
def generate_hmac(data, secret)
|
192
|
-
OpenSSL::HMAC.hexdigest(
|
193
|
+
OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
|
194
|
+
end
|
195
|
+
|
196
|
+
def secure?(options)
|
197
|
+
@secrets.size >= 1 ||
|
198
|
+
(options[:coder] && options[:let_coder_handle_secure_encoding])
|
193
199
|
end
|
194
200
|
|
195
201
|
end
|