wycats-merb-core 0.9.8
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.
- data/CHANGELOG +992 -0
- data/CONTRIBUTORS +94 -0
- data/LICENSE +20 -0
- data/PUBLIC_CHANGELOG +142 -0
- data/README +21 -0
- data/Rakefile +458 -0
- data/TODO +0 -0
- data/bin/merb +11 -0
- data/bin/merb-specs +5 -0
- data/lib/merb-core.rb +598 -0
- data/lib/merb-core/autoload.rb +31 -0
- data/lib/merb-core/bootloader.rb +717 -0
- data/lib/merb-core/config.rb +305 -0
- data/lib/merb-core/constants.rb +45 -0
- data/lib/merb-core/controller/abstract_controller.rb +568 -0
- data/lib/merb-core/controller/exceptions.rb +315 -0
- data/lib/merb-core/controller/merb_controller.rb +256 -0
- data/lib/merb-core/controller/mime.rb +107 -0
- data/lib/merb-core/controller/mixins/authentication.rb +123 -0
- data/lib/merb-core/controller/mixins/conditional_get.rb +83 -0
- data/lib/merb-core/controller/mixins/controller.rb +319 -0
- data/lib/merb-core/controller/mixins/render.rb +513 -0
- data/lib/merb-core/controller/mixins/responder.rb +469 -0
- data/lib/merb-core/controller/template.rb +254 -0
- data/lib/merb-core/core_ext.rb +9 -0
- data/lib/merb-core/core_ext/hash.rb +7 -0
- data/lib/merb-core/core_ext/kernel.rb +340 -0
- data/lib/merb-core/dispatch/cookies.rb +130 -0
- data/lib/merb-core/dispatch/default_exception/default_exception.rb +93 -0
- data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +198 -0
- data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +73 -0
- data/lib/merb-core/dispatch/default_exception/views/index.html.erb +94 -0
- data/lib/merb-core/dispatch/dispatcher.rb +176 -0
- data/lib/merb-core/dispatch/request.rb +729 -0
- data/lib/merb-core/dispatch/router.rb +151 -0
- data/lib/merb-core/dispatch/router/behavior.rb +566 -0
- data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
- data/lib/merb-core/dispatch/router/resources.rb +191 -0
- data/lib/merb-core/dispatch/router/route.rb +511 -0
- data/lib/merb-core/dispatch/session.rb +222 -0
- data/lib/merb-core/dispatch/session/container.rb +74 -0
- data/lib/merb-core/dispatch/session/cookie.rb +173 -0
- data/lib/merb-core/dispatch/session/memcached.rb +68 -0
- data/lib/merb-core/dispatch/session/memory.rb +99 -0
- data/lib/merb-core/dispatch/session/store_container.rb +150 -0
- data/lib/merb-core/dispatch/worker.rb +28 -0
- data/lib/merb-core/gem_ext/erubis.rb +77 -0
- data/lib/merb-core/logger.rb +203 -0
- data/lib/merb-core/plugins.rb +67 -0
- data/lib/merb-core/rack.rb +25 -0
- data/lib/merb-core/rack/adapter.rb +44 -0
- data/lib/merb-core/rack/adapter/ebb.rb +25 -0
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
- data/lib/merb-core/rack/adapter/irb.rb +118 -0
- data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/runner.rb +28 -0
- data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/thin.rb +39 -0
- data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
- data/lib/merb-core/rack/adapter/webrick.rb +36 -0
- data/lib/merb-core/rack/application.rb +32 -0
- data/lib/merb-core/rack/handler/mongrel.rb +97 -0
- data/lib/merb-core/rack/middleware.rb +20 -0
- data/lib/merb-core/rack/middleware/conditional_get.rb +29 -0
- data/lib/merb-core/rack/middleware/content_length.rb +18 -0
- data/lib/merb-core/rack/middleware/csrf.rb +73 -0
- data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
- data/lib/merb-core/rack/middleware/profiler.rb +19 -0
- data/lib/merb-core/rack/middleware/static.rb +45 -0
- data/lib/merb-core/rack/middleware/tracer.rb +20 -0
- data/lib/merb-core/server.rb +284 -0
- data/lib/merb-core/tasks/audit.rake +68 -0
- data/lib/merb-core/tasks/gem_management.rb +229 -0
- data/lib/merb-core/tasks/merb.rb +1 -0
- data/lib/merb-core/tasks/merb_rake_helper.rb +80 -0
- data/lib/merb-core/tasks/stats.rake +71 -0
- data/lib/merb-core/test.rb +11 -0
- data/lib/merb-core/test/helpers.rb +9 -0
- data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
- data/lib/merb-core/test/helpers/request_helper.rb +393 -0
- data/lib/merb-core/test/helpers/route_helper.rb +39 -0
- data/lib/merb-core/test/helpers/view_helper.rb +121 -0
- data/lib/merb-core/test/matchers.rb +9 -0
- data/lib/merb-core/test/matchers/controller_matchers.rb +351 -0
- data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
- data/lib/merb-core/test/matchers/view_matchers.rb +375 -0
- data/lib/merb-core/test/run_specs.rb +49 -0
- data/lib/merb-core/test/tasks/spectasks.rb +68 -0
- data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
- data/lib/merb-core/test/test_ext/object.rb +14 -0
- data/lib/merb-core/test/test_ext/string.rb +14 -0
- data/lib/merb-core/vendor/facets.rb +2 -0
- data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
- data/lib/merb-core/vendor/facets/inflect.rb +342 -0
- data/lib/merb-core/version.rb +3 -0
- metadata +253 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
require 'merb-core/dispatch/session/container'
|
2
|
+
require 'merb-core/dispatch/session/store_container'
|
3
|
+
|
4
|
+
module Merb
|
5
|
+
class Config
|
6
|
+
# Returns stores list constructed from
|
7
|
+
# configured session stores (:session_stores config option)
|
8
|
+
# or default one (:session_store config option).
|
9
|
+
def self.session_stores
|
10
|
+
@session_stores ||= begin
|
11
|
+
config_stores = Array(
|
12
|
+
Merb::Config[:session_stores] || Merb::Config[:session_store]
|
13
|
+
)
|
14
|
+
config_stores.map { |name| name.to_sym }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end # Config
|
18
|
+
|
19
|
+
# The Merb::Session module gets mixed into Merb::SessionContainer to allow
|
20
|
+
# app-level functionality (usually found in app/models/merb/session.rb) for
|
21
|
+
# session.
|
22
|
+
#
|
23
|
+
# You can use this module to implement additional methods to simplify
|
24
|
+
# building wizard-like application components,
|
25
|
+
# authentication frameworks, etc.
|
26
|
+
module Session
|
27
|
+
end
|
28
|
+
|
29
|
+
# This is mixed into Merb::Controller on framework boot.
|
30
|
+
module SessionMixin
|
31
|
+
# Raised when no suitable session store has been setup.
|
32
|
+
class NoSessionContainer < StandardError; end
|
33
|
+
|
34
|
+
# Raised when storing more data than the available space reserved.
|
35
|
+
class SessionOverflow < StandardError; end
|
36
|
+
|
37
|
+
# Session configuration options:
|
38
|
+
#
|
39
|
+
# :session_id_key The key by which a session value/id is
|
40
|
+
# retrieved; defaults to _session_id
|
41
|
+
#
|
42
|
+
# :session_expiry When to expire the session cookie;
|
43
|
+
# defaults to 2 weeks
|
44
|
+
#
|
45
|
+
# :session_secret_key A secret string which is used to sign/validate
|
46
|
+
# session data; min. 16 chars
|
47
|
+
#
|
48
|
+
# :default_cookie_domain The default domain to write cookies for.
|
49
|
+
def self.included(base)
|
50
|
+
# Register a callback to finalize sessions - needs to run before the cookie
|
51
|
+
# callback extracts Set-Cookie headers from request.cookies.
|
52
|
+
base._after_dispatch_callbacks.unshift lambda { |c| c.request.finalize_session }
|
53
|
+
end
|
54
|
+
|
55
|
+
# ==== Parameters
|
56
|
+
# session_store<String>:: The type of session store to access.
|
57
|
+
#
|
58
|
+
# ==== Returns
|
59
|
+
# SessionContainer:: The session that was extracted from the request object.
|
60
|
+
def session(session_store = nil)
|
61
|
+
request.session(session_store)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Module methods
|
65
|
+
|
66
|
+
# ==== Returns
|
67
|
+
# String:: A random 32 character string for use as a unique session ID.
|
68
|
+
def rand_uuid
|
69
|
+
values = [
|
70
|
+
rand(0x0010000),
|
71
|
+
rand(0x0010000),
|
72
|
+
rand(0x0010000),
|
73
|
+
rand(0x0010000),
|
74
|
+
rand(0x0010000),
|
75
|
+
rand(0x1000000),
|
76
|
+
rand(0x1000000),
|
77
|
+
]
|
78
|
+
"%04x%04x%04x%04x%04x%06x%06x" % values
|
79
|
+
end
|
80
|
+
|
81
|
+
# Marks this session as needing a new cookie.
|
82
|
+
def needs_new_cookie!
|
83
|
+
@_new_cookie = true
|
84
|
+
end
|
85
|
+
|
86
|
+
def needs_new_cookie?
|
87
|
+
@_new_cookie
|
88
|
+
end
|
89
|
+
|
90
|
+
module_function :rand_uuid, :needs_new_cookie!, :needs_new_cookie?
|
91
|
+
|
92
|
+
module RequestMixin
|
93
|
+
|
94
|
+
def self.included(base)
|
95
|
+
base.extend ClassMethods
|
96
|
+
|
97
|
+
# Keep track of all known session store types.
|
98
|
+
base.cattr_accessor :registered_session_types
|
99
|
+
base.registered_session_types = Dictionary.new
|
100
|
+
base.class_inheritable_accessor :_session_id_key, :_session_secret_key,
|
101
|
+
:_session_expiry
|
102
|
+
|
103
|
+
base._session_id_key = Merb::Config[:session_id_key] || '_session_id'
|
104
|
+
base._session_expiry = Merb::Config[:session_expiry] || 0
|
105
|
+
base._session_secret_key = Merb::Config[:session_secret_key]
|
106
|
+
end
|
107
|
+
|
108
|
+
module ClassMethods
|
109
|
+
|
110
|
+
# ==== Parameters
|
111
|
+
# name<~to_sym>:: Name of the session type to register.
|
112
|
+
# class_name<String>:: The corresponding class name.
|
113
|
+
#
|
114
|
+
# === Notres
|
115
|
+
# This is automatically called when Merb::SessionContainer is subclassed.
|
116
|
+
def register_session_type(name, class_name)
|
117
|
+
self.registered_session_types[name.to_sym] = class_name
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# The default session store type.
|
123
|
+
def default_session_store
|
124
|
+
Merb::Config[:session_store] && Merb::Config[:session_store].to_sym
|
125
|
+
end
|
126
|
+
|
127
|
+
# ==== Returns
|
128
|
+
# Hash:: All active session stores by type.
|
129
|
+
def session_stores
|
130
|
+
@session_stores ||= {}
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns session container. Merb is able to handle multiple session
|
134
|
+
# stores, hence a parameter to pick it.
|
135
|
+
#
|
136
|
+
# ==== Parameters
|
137
|
+
# session_store<String>:: The type of session store to access,
|
138
|
+
# defaults to default_session_store.
|
139
|
+
#
|
140
|
+
# === Notes
|
141
|
+
# If no suitable session store type is given, it defaults to
|
142
|
+
# cookie-based sessions.
|
143
|
+
def session(session_store = nil)
|
144
|
+
session_store ||= default_session_store
|
145
|
+
if class_name = self.class.registered_session_types[session_store]
|
146
|
+
session_stores[session_store] ||= Object.full_const_get(class_name).setup(self)
|
147
|
+
elsif fallback = self.class.registered_session_types.keys.first
|
148
|
+
Merb.logger.warn "Session store '#{session_store}' not found. Check your configuration in init file."
|
149
|
+
Merb.logger.warn "Falling back to #{fallback} session store."
|
150
|
+
session(fallback)
|
151
|
+
else
|
152
|
+
msg = "No session store set. Set it in init file like this: c[:session_store] = 'activerecord'"
|
153
|
+
Merb.logger.error!(msg)
|
154
|
+
raise NoSessionContainer, msg
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# ==== Parameters
|
159
|
+
# new_session<Merb::SessionContainer>:: A session store instance.
|
160
|
+
#
|
161
|
+
# === Notes
|
162
|
+
# The session is assigned internally by its session_store_type key.
|
163
|
+
def session=(new_session)
|
164
|
+
if self.session?(new_session.class.session_store_type)
|
165
|
+
original_session_id = self.session(new_session.class.session_store_type).session_id
|
166
|
+
if new_session.session_id != original_session_id
|
167
|
+
set_session_id_cookie(new_session.session_id)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
session_stores[new_session.class.session_store_type] = new_session
|
171
|
+
end
|
172
|
+
|
173
|
+
# Whether a session has been setup
|
174
|
+
def session?(session_store = nil)
|
175
|
+
(session_store ? [session_store] : session_stores).any? do |type, store|
|
176
|
+
store.is_a?(Merb::SessionContainer)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Teardown and/or persist the current sessions.
|
181
|
+
def finalize_session
|
182
|
+
session_stores.each { |name, store| store.finalize(self) }
|
183
|
+
end
|
184
|
+
alias :finalize_sessions :finalize_session
|
185
|
+
|
186
|
+
# Assign default cookie values
|
187
|
+
def default_cookies
|
188
|
+
defaults = {}
|
189
|
+
if route && route.allow_fixation? && params.key?(_session_id_key)
|
190
|
+
Merb.logger.info("Fixated session id: #{_session_id_key}")
|
191
|
+
defaults[_session_id_key] = params[_session_id_key]
|
192
|
+
end
|
193
|
+
defaults
|
194
|
+
end
|
195
|
+
|
196
|
+
# Sets session cookie value.
|
197
|
+
#
|
198
|
+
# ==== Parameters
|
199
|
+
# value<String>:: The value of the session cookie; either the session id or the actual encoded data.
|
200
|
+
# options<Hash>:: Cookie options like domain, path and expired.
|
201
|
+
def set_session_cookie_value(value, options = {})
|
202
|
+
defaults = {}
|
203
|
+
defaults[:expires] = Time.now + _session_expiry if _session_expiry > 0
|
204
|
+
cookies.set_cookie(_session_id_key, value, defaults.merge(options))
|
205
|
+
end
|
206
|
+
alias :set_session_id_cookie :set_session_cookie_value
|
207
|
+
|
208
|
+
# ==== Returns
|
209
|
+
# String:: The value of the session cookie; either the session id or the actual encoded data.
|
210
|
+
def session_cookie_value
|
211
|
+
cookies[_session_id_key]
|
212
|
+
end
|
213
|
+
alias :session_id :session_cookie_value
|
214
|
+
|
215
|
+
# Destroy the session cookie.
|
216
|
+
def destroy_session_cookie
|
217
|
+
cookies.delete(_session_id_key)
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Merb
|
2
|
+
class SessionContainer < Mash
|
3
|
+
|
4
|
+
class_inheritable_accessor :session_store_type
|
5
|
+
cattr_accessor :subclasses
|
6
|
+
self.subclasses = []
|
7
|
+
|
8
|
+
attr_reader :session_id
|
9
|
+
attr_accessor :needs_new_cookie
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
# Register the subclass as an available session store type.
|
14
|
+
def inherited(klass)
|
15
|
+
self.subclasses << klass.to_s
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generates a new session ID and creates a new session.
|
20
|
+
#
|
21
|
+
# ==== Returns
|
22
|
+
# SessionContainer:: The new session.
|
23
|
+
def generate
|
24
|
+
end
|
25
|
+
|
26
|
+
# ==== Parameters
|
27
|
+
# request<Merb::Request>:: The Merb::Request that came in from Rack.
|
28
|
+
#
|
29
|
+
# ==== Returns
|
30
|
+
# SessionContainer:: a SessionContainer. If no sessions were found,
|
31
|
+
# a new SessionContainer will be generated.
|
32
|
+
def setup(request)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# ==== Parameters
|
38
|
+
# session_id<String>:: A unique identifier for this session.
|
39
|
+
def initialize(session_id)
|
40
|
+
@_destroy = false
|
41
|
+
self.session_id = session_id
|
42
|
+
end
|
43
|
+
|
44
|
+
# Assign a new session_id.
|
45
|
+
#
|
46
|
+
# Recreates the cookie with the default expiration time. Useful during log
|
47
|
+
# in for pushing back the expiration date.
|
48
|
+
def session_id=(sid)
|
49
|
+
self.needs_new_cookie = (@session_id && @session_id != sid)
|
50
|
+
@session_id = sid
|
51
|
+
end
|
52
|
+
|
53
|
+
# Teardown and/or persist the current session.
|
54
|
+
#
|
55
|
+
# If @_destroy is true, clear out the session completely, including
|
56
|
+
# removal of the session cookie itself.
|
57
|
+
#
|
58
|
+
# ==== Parameters
|
59
|
+
# request<Merb::Request>:: The Merb::Request that came in from Rack.
|
60
|
+
def finalize(request)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Destroy the current session - clears data and removes session cookie.
|
64
|
+
def clear!
|
65
|
+
@_destroy = true
|
66
|
+
self.clear
|
67
|
+
end
|
68
|
+
|
69
|
+
# Regenerate the session_id.
|
70
|
+
def regenerate
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'base64' # to convert Marshal.dump to ASCII
|
2
|
+
require 'openssl' # to generate the HMAC message digest
|
3
|
+
module Merb
|
4
|
+
|
5
|
+
# If you have more than 4K of session data or don't want your data to be
|
6
|
+
# visible to the user, pick another session store.
|
7
|
+
#
|
8
|
+
# CookieOverflow is raised if you attempt to store more than 4K of data.
|
9
|
+
# TamperedWithCookie is raised if the data integrity check fails.
|
10
|
+
#
|
11
|
+
# A message digest is included with the cookie to ensure data integrity:
|
12
|
+
# a user cannot alter session data without knowing the secret key included
|
13
|
+
# in the hash.
|
14
|
+
#
|
15
|
+
# To use Cookie Sessions, set in config/merb.yml
|
16
|
+
# :session_secret_key - your secret digest key
|
17
|
+
# :session_store: cookie
|
18
|
+
class CookieSession < SessionContainer
|
19
|
+
# TODO (maybe):
|
20
|
+
# include request ip address
|
21
|
+
# AES encrypt marshaled data
|
22
|
+
|
23
|
+
# Raised when storing more than 4K of session data.
|
24
|
+
class CookieOverflow < StandardError; end
|
25
|
+
|
26
|
+
# Raised when the cookie fails its integrity check.
|
27
|
+
class TamperedWithCookie < StandardError; end
|
28
|
+
|
29
|
+
# Cookies can typically store 4096 bytes.
|
30
|
+
MAX = 4096
|
31
|
+
DIGEST = OpenSSL::Digest::Digest.new('SHA1') # or MD5, RIPEMD160, SHA256?
|
32
|
+
|
33
|
+
attr_accessor :_original_session_data
|
34
|
+
|
35
|
+
# The session store type
|
36
|
+
self.session_store_type = :cookie
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# Generates a new session ID and creates a new session.
|
40
|
+
#
|
41
|
+
# ==== Returns
|
42
|
+
# SessionContainer:: The new session.
|
43
|
+
def generate
|
44
|
+
self.new(Merb::SessionMixin.rand_uuid, "", Merb::Request._session_secret_key)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Set up a new session on request: make it available on request instance.
|
48
|
+
#
|
49
|
+
# ==== Parameters
|
50
|
+
# request<Merb::Request>:: The Merb::Request that came in from Rack.
|
51
|
+
#
|
52
|
+
# ==== Returns
|
53
|
+
# SessionContainer:: a SessionContainer. If no sessions were found,
|
54
|
+
# a new SessionContainer will be generated.
|
55
|
+
def setup(request)
|
56
|
+
session = self.new(Merb::SessionMixin.rand_uuid,
|
57
|
+
request.session_cookie_value, request._session_secret_key)
|
58
|
+
session._original_session_data = session.to_cookie
|
59
|
+
request.session = session
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# ==== Parameters
|
65
|
+
# session_id<String>:: A unique identifier for this session.
|
66
|
+
# cookie<String>:: The raw cookie data.
|
67
|
+
# secret<String>:: A session secret.
|
68
|
+
#
|
69
|
+
# ==== Raises
|
70
|
+
# ArgumentError:: Nil or blank secret.
|
71
|
+
def initialize(session_id, cookie, secret)
|
72
|
+
super session_id
|
73
|
+
if secret.blank? || secret.length < 16
|
74
|
+
msg = "You must specify a session_secret_key in your init file, and it must be at least 16 characters"
|
75
|
+
Merb.logger.warn(msg)
|
76
|
+
raise ArgumentError, msg
|
77
|
+
end
|
78
|
+
@secret = secret
|
79
|
+
self.update(unmarshal(cookie))
|
80
|
+
end
|
81
|
+
|
82
|
+
# Teardown and/or persist the current session.
|
83
|
+
#
|
84
|
+
# If @_destroy is true, clear out the session completely, including
|
85
|
+
# removal of the session cookie itself.
|
86
|
+
#
|
87
|
+
# ==== Parameters
|
88
|
+
# request<Merb::Request>:: request object created from Rack environment.
|
89
|
+
def finalize(request)
|
90
|
+
if @_destroy
|
91
|
+
request.destroy_session_cookie
|
92
|
+
elsif _original_session_data != (new_session_data = self.to_cookie)
|
93
|
+
request.set_session_cookie_value(new_session_data)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Regenerate the session_id.
|
98
|
+
def regenerate
|
99
|
+
self.session_id = Merb::SessionMixin.rand_uuid
|
100
|
+
end
|
101
|
+
|
102
|
+
# Create the raw cookie string; includes an HMAC keyed message digest.
|
103
|
+
#
|
104
|
+
# ==== Returns
|
105
|
+
# String:: Cookie value.
|
106
|
+
#
|
107
|
+
# ==== Raises
|
108
|
+
# CookieOverflow:: More than 4K of data put into session.
|
109
|
+
#
|
110
|
+
# ==== Notes
|
111
|
+
# Session data is converted to a Hash first, since a container might
|
112
|
+
# choose to marshal it, which would make it persist
|
113
|
+
# attributes like 'needs_new_cookie', which it shouldn't.
|
114
|
+
def to_cookie
|
115
|
+
unless self.empty?
|
116
|
+
data = self.serialize
|
117
|
+
value = Merb::Request.escape "#{data}--#{generate_digest(data)}"
|
118
|
+
if value.size > MAX
|
119
|
+
msg = "Cookies have limit of 4K. Session contents: #{data.inspect}"
|
120
|
+
Merb.logger.error!(msg)
|
121
|
+
raise CookieOverflow, msg
|
122
|
+
end
|
123
|
+
value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Generate the HMAC keyed message digest. Uses SHA1.
|
130
|
+
def generate_digest(data)
|
131
|
+
OpenSSL::HMAC.hexdigest(DIGEST, @secret, data)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Unmarshal cookie data to a hash and verify its integrity.
|
135
|
+
#
|
136
|
+
# ==== Parameters
|
137
|
+
# cookie<~to_s>:: The cookie to unmarshal.
|
138
|
+
#
|
139
|
+
# ==== Raises
|
140
|
+
# TamperedWithCookie:: The digests don't match.
|
141
|
+
#
|
142
|
+
# ==== Returns
|
143
|
+
# Hash:: The stored session data.
|
144
|
+
def unmarshal(cookie)
|
145
|
+
if cookie.blank?
|
146
|
+
{}
|
147
|
+
else
|
148
|
+
data, digest = Merb::Request.unescape(cookie).split('--')
|
149
|
+
return {} if data.blank? || digest.blank?
|
150
|
+
unless digest == generate_digest(data)
|
151
|
+
clear
|
152
|
+
unless Merb::Config[:ignore_tampered_cookies]
|
153
|
+
raise TamperedWithCookie, "Maybe the site's session_secret_key has changed?"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
unserialize(data)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
protected
|
161
|
+
|
162
|
+
# Serialize current session data as a Hash.
|
163
|
+
# Uses Base64 encoding for integrity.
|
164
|
+
def serialize
|
165
|
+
Base64.encode64(Marshal.dump(self.to_hash)).chop
|
166
|
+
end
|
167
|
+
|
168
|
+
# Unserialize the raw cookie data to a Hash
|
169
|
+
def unserialize(data)
|
170
|
+
Marshal.load(Base64.decode64(data)) rescue {}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|