/
cookie_store.rb
128 lines (114 loc) · 4.61 KB
/
cookie_store.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# frozen_string_literal: true
require "active_support/core_ext/hash/keys"
require "action_dispatch/middleware/session/abstract_store"
require "rack/session/cookie"
module ActionDispatch
module Session
# = Action Dispatch Session \CookieStore
#
# This cookie-based session store is the \Rails default. It is
# dramatically faster than the alternatives.
#
# Sessions typically contain at most a user ID and flash message; both fit
# within the 4096 bytes cookie size limit. A +CookieOverflow+ exception is raised if
# you attempt to store more than 4096 bytes of data.
#
# The cookie jar used for storage is automatically configured to be the
# best possible option given your application's configuration.
#
# Your cookies will be encrypted using your application's +secret_key_base+. This
# goes a step further than signed cookies in that encrypted cookies cannot
# be altered or read by users. This is the default starting in \Rails 4.
#
# Configure your session store in an initializer:
#
# Rails.application.config.session_store :cookie_store, key: '_your_app_session'
#
# In the development and test environments your application's +secret_key_base+ is
# generated by \Rails and stored in a temporary file in <tt>tmp/local_secret.txt</tt>.
# In all other environments, it is stored encrypted in the
# <tt>config/credentials.yml.enc</tt> file.
#
# If your application was not updated to \Rails 5.2 defaults, the +secret_key_base+
# will be found in the old <tt>config/secrets.yml</tt> file.
#
# Note that changing your +secret_key_base+ will invalidate all existing session.
# Additionally, you should take care to make sure you are not relying on the
# ability to decode signed cookies generated by your app in external
# applications or JavaScript before changing it.
#
# Because CookieStore extends +Rack::Session::Abstract::Persisted+, many of the
# options described there can be used to customize the session cookie that
# is generated. For example:
#
# Rails.application.config.session_store :cookie_store, expire_after: 14.days
#
# would set the session cookie to expire automatically 14 days after creation.
# Other useful options include <tt>:key</tt>, <tt>:secure</tt>,
# <tt>:httponly</tt>, and <tt>:same_site</tt>.
class CookieStore < AbstractSecureStore
class SessionId < DelegateClass(Rack::Session::SessionId)
attr_reader :cookie_value
def initialize(session_id, cookie_value = {})
super(session_id)
@cookie_value = cookie_value
end
end
DEFAULT_SAME_SITE = proc { |request| request.cookies_same_site_protection } # :nodoc:
def initialize(app, options = {})
options[:cookie_only] = true
options[:same_site] = DEFAULT_SAME_SITE if !options.key?(:same_site)
super
end
def delete_session(req, session_id, options)
new_sid = generate_sid unless options[:drop]
# Reset hash and Assign the new session id
req.set_header("action_dispatch.request.unsigned_session_cookie", new_sid ? { "session_id" => new_sid.public_id } : {})
new_sid
end
def load_session(req)
stale_session_check! do
data = unpacked_cookie_data(req)
data = persistent_session_id!(data)
[Rack::Session::SessionId.new(data["session_id"]), data]
end
end
private
def extract_session_id(req)
stale_session_check! do
sid = unpacked_cookie_data(req)["session_id"]
sid && Rack::Session::SessionId.new(sid)
end
end
def unpacked_cookie_data(req)
req.fetch_header("action_dispatch.request.unsigned_session_cookie") do |k|
v = stale_session_check! do
if data = get_cookie(req)
data.stringify_keys!
end
data || {}
end
req.set_header k, v
end
end
def persistent_session_id!(data, sid = nil)
data ||= {}
data["session_id"] ||= sid || generate_sid.public_id
data
end
def write_session(req, sid, session_data, options)
session_data["session_id"] = sid.public_id
SessionId.new(sid, session_data)
end
def set_cookie(request, session_id, cookie)
cookie_jar(request)[@key] = cookie
end
def get_cookie(req)
cookie_jar(req)[@key]
end
def cookie_jar(request)
request.cookie_jar.signed_or_encrypted
end
end
end
end