halorgium-actionpack 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,26 @@
1
+ module ActionDispatch
2
+ class Rescue
3
+ def initialize(app, rescuers = {}, &block)
4
+ @app, @rescuers = app, {}
5
+ rescuers.each { |exception, rescuer| rescue_from(exception, rescuer) }
6
+ instance_eval(&block) if block_given?
7
+ end
8
+
9
+ def call(env)
10
+ @app.call(env)
11
+ rescue Exception => exception
12
+ if rescuer = @rescuers[exception.class.name]
13
+ env['action_dispatch.rescue.exception'] = exception
14
+ rescuer.call(env)
15
+ else
16
+ raise exception
17
+ end
18
+ end
19
+
20
+ protected
21
+ def rescue_from(exception, rescuer)
22
+ exception = exception.class.name if exception.is_a?(Exception)
23
+ @rescuers[exception.to_s] = rescuer
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,208 @@
1
+ require 'rack/utils'
2
+
3
+ module ActionDispatch
4
+ module Session
5
+ class SessionRestoreError < StandardError #:nodoc:
6
+ end
7
+
8
+ class AbstractStore
9
+ ENV_SESSION_KEY = 'rack.session'.freeze
10
+ ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
11
+
12
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
13
+ SET_COOKIE = 'Set-Cookie'.freeze
14
+
15
+ class SessionHash < Hash
16
+ def initialize(by, env)
17
+ super()
18
+ @by = by
19
+ @env = env
20
+ @loaded = false
21
+ end
22
+
23
+ def session_id
24
+ ActiveSupport::Deprecation.warn(
25
+ "ActionDispatch::Session::AbstractStore::SessionHash#session_id " +
26
+ "has been deprecated. Please use request.session_options[:id] instead.", caller)
27
+ @env[ENV_SESSION_OPTIONS_KEY][:id]
28
+ end
29
+
30
+ def [](key)
31
+ load! unless @loaded
32
+ super(key.to_s)
33
+ end
34
+
35
+ def []=(key, value)
36
+ load! unless @loaded
37
+ super(key.to_s, value)
38
+ end
39
+
40
+ def to_hash
41
+ h = {}.replace(self)
42
+ h.delete_if { |k,v| v.nil? }
43
+ h
44
+ end
45
+
46
+ def update(hash = nil)
47
+ if hash.nil?
48
+ ActiveSupport::Deprecation.warn('use replace instead', caller)
49
+ replace({})
50
+ else
51
+ load! unless @loaded
52
+ super(hash.stringify_keys)
53
+ end
54
+ end
55
+
56
+ def delete(key = nil)
57
+ if key.nil?
58
+ ActiveSupport::Deprecation.warn('use clear instead', caller)
59
+ clear
60
+ else
61
+ load! unless @loaded
62
+ super(key.to_s)
63
+ end
64
+ end
65
+
66
+ def data
67
+ ActiveSupport::Deprecation.warn(
68
+ "ActionDispatch::Session::AbstractStore::SessionHash#data " +
69
+ "has been deprecated. Please use #to_hash instead.", caller)
70
+ to_hash
71
+ end
72
+
73
+ def close
74
+ ActiveSupport::Deprecation.warn('sessions should no longer be closed', caller)
75
+ end
76
+
77
+ def inspect
78
+ load! unless @loaded
79
+ super
80
+ end
81
+
82
+ private
83
+ def loaded?
84
+ @loaded
85
+ end
86
+
87
+ def load!
88
+ stale_session_check! do
89
+ id, session = @by.send(:load_session, @env)
90
+ (@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id
91
+ replace(session.stringify_keys)
92
+ @loaded = true
93
+ end
94
+ end
95
+
96
+ def stale_session_check!
97
+ yield
98
+ rescue ArgumentError => argument_error
99
+ if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
100
+ begin
101
+ # Note that the regexp does not allow $1 to end with a ':'
102
+ $1.constantize
103
+ rescue LoadError, NameError => const_error
104
+ raise ActionDispatch::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
105
+ end
106
+
107
+ retry
108
+ else
109
+ raise
110
+ end
111
+ end
112
+ end
113
+
114
+ DEFAULT_OPTIONS = {
115
+ :key => '_session_id',
116
+ :path => '/',
117
+ :domain => nil,
118
+ :expire_after => nil,
119
+ :secure => false,
120
+ :httponly => true,
121
+ :cookie_only => true
122
+ }
123
+
124
+ def initialize(app, options = {})
125
+ # Process legacy CGI options
126
+ options = options.symbolize_keys
127
+ if options.has_key?(:session_path)
128
+ options[:path] = options.delete(:session_path)
129
+ end
130
+ if options.has_key?(:session_key)
131
+ options[:key] = options.delete(:session_key)
132
+ end
133
+ if options.has_key?(:session_http_only)
134
+ options[:httponly] = options.delete(:session_http_only)
135
+ end
136
+
137
+ @app = app
138
+ @default_options = DEFAULT_OPTIONS.merge(options)
139
+ @key = @default_options[:key]
140
+ @cookie_only = @default_options[:cookie_only]
141
+ end
142
+
143
+ def call(env)
144
+ session = SessionHash.new(self, env)
145
+
146
+ env[ENV_SESSION_KEY] = session
147
+ env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
148
+
149
+ response = @app.call(env)
150
+
151
+ session_data = env[ENV_SESSION_KEY]
152
+ options = env[ENV_SESSION_OPTIONS_KEY]
153
+
154
+ if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
155
+ session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
156
+
157
+ sid = options[:id] || generate_sid
158
+
159
+ unless set_session(env, sid, session_data.to_hash)
160
+ return response
161
+ end
162
+
163
+ cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
164
+ cookie << "; domain=#{options[:domain]}" if options[:domain]
165
+ cookie << "; path=#{options[:path]}" if options[:path]
166
+ if options[:expire_after]
167
+ expiry = Time.now + options[:expire_after]
168
+ cookie << "; expires=#{expiry.httpdate}"
169
+ end
170
+ cookie << "; Secure" if options[:secure]
171
+ cookie << "; HttpOnly" if options[:httponly]
172
+
173
+ headers = response[1]
174
+ unless headers[SET_COOKIE].blank?
175
+ headers[SET_COOKIE] << "\n#{cookie}"
176
+ else
177
+ headers[SET_COOKIE] = cookie
178
+ end
179
+ end
180
+
181
+ response
182
+ end
183
+
184
+ private
185
+ def generate_sid
186
+ ActiveSupport::SecureRandom.hex(16)
187
+ end
188
+
189
+ def load_session(env)
190
+ request = Rack::Request.new(env)
191
+ sid = request.cookies[@key]
192
+ unless @cookie_only
193
+ sid ||= request.params[@key]
194
+ end
195
+ sid, session = get_session(env, sid)
196
+ [sid, session]
197
+ end
198
+
199
+ def get_session(env, sid)
200
+ raise '#get_session needs to be implemented.'
201
+ end
202
+
203
+ def set_session(env, sid, session_data)
204
+ raise '#set_session needs to be implemented.'
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,235 @@
1
+ require "active_support/core_ext/hash/keys"
2
+
3
+ module ActionDispatch
4
+ module Session
5
+ # This cookie-based session store is the Rails default. Sessions typically
6
+ # contain at most a user_id and flash message; both fit within the 4K cookie
7
+ # size limit. Cookie-based sessions are dramatically faster than the
8
+ # alternatives.
9
+ #
10
+ # If you have more than 4K of session data or don't want your data to be
11
+ # visible to the user, pick another session store.
12
+ #
13
+ # CookieOverflow is raised if you attempt to store more than 4K of data.
14
+ #
15
+ # A message digest is included with the cookie to ensure data integrity:
16
+ # a user cannot alter his +user_id+ without knowing the secret key
17
+ # included in the hash. New apps are generated with a pregenerated secret
18
+ # in config/environment.rb. Set your own for old apps you're upgrading.
19
+ #
20
+ # Session options:
21
+ #
22
+ # * <tt>:secret</tt>: An application-wide key string or block returning a
23
+ # string called per generated digest. The block is called with the
24
+ # CGI::Session instance as an argument. It's important that the secret
25
+ # is not vulnerable to a dictionary attack. Therefore, you should choose
26
+ # a secret consisting of random numbers and letters and more than 30
27
+ # characters. Examples:
28
+ #
29
+ # :secret => '449fe2e7daee471bffae2fd8dc02313d'
30
+ # :secret => Proc.new { User.current_user.secret_key }
31
+ #
32
+ # * <tt>:digest</tt>: The message digest algorithm used to verify session
33
+ # integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
34
+ # such as 'MD5', 'RIPEMD160', 'SHA256', etc.
35
+ #
36
+ # To generate a secret key for an existing application, run
37
+ # "rake secret" and set the key in config/environment.rb.
38
+ #
39
+ # Note that changing digest or secret invalidates all existing sessions!
40
+ class CookieStore
41
+ # Cookies can typically store 4096 bytes.
42
+ MAX = 4096
43
+ SECRET_MIN_LENGTH = 30 # characters
44
+
45
+ DEFAULT_OPTIONS = {
46
+ :key => '_session_id',
47
+ :domain => nil,
48
+ :path => "/",
49
+ :expire_after => nil,
50
+ :httponly => true
51
+ }.freeze
52
+
53
+ class OptionsHash < Hash
54
+ def initialize(by, env, default_options)
55
+ @session_data = env[CookieStore::ENV_SESSION_KEY]
56
+ default_options.each { |key, value| self[key] = value }
57
+ end
58
+
59
+ def [](key)
60
+ key == :id ? @session_data[:session_id] : super(key)
61
+ end
62
+ end
63
+
64
+ ENV_SESSION_KEY = "rack.session".freeze
65
+ ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
66
+ HTTP_SET_COOKIE = "Set-Cookie".freeze
67
+
68
+ # Raised when storing more than 4K of session data.
69
+ class CookieOverflow < StandardError; end
70
+
71
+ def initialize(app, options = {})
72
+ # Process legacy CGI options
73
+ options = options.symbolize_keys
74
+ if options.has_key?(:session_path)
75
+ options[:path] = options.delete(:session_path)
76
+ end
77
+ if options.has_key?(:session_key)
78
+ options[:key] = options.delete(:session_key)
79
+ end
80
+ if options.has_key?(:session_http_only)
81
+ options[:httponly] = options.delete(:session_http_only)
82
+ end
83
+
84
+ @app = app
85
+
86
+ # The session_key option is required.
87
+ ensure_session_key(options[:key])
88
+ @key = options.delete(:key).freeze
89
+
90
+ # The secret option is required.
91
+ ensure_secret_secure(options[:secret])
92
+ @secret = options.delete(:secret).freeze
93
+
94
+ @digest = options.delete(:digest) || 'SHA1'
95
+ @verifier = verifier_for(@secret, @digest)
96
+
97
+ @default_options = DEFAULT_OPTIONS.merge(options).freeze
98
+
99
+ freeze
100
+ end
101
+
102
+ def call(env)
103
+ env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
104
+ env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
105
+
106
+ status, headers, body = @app.call(env)
107
+
108
+ session_data = env[ENV_SESSION_KEY]
109
+ options = env[ENV_SESSION_OPTIONS_KEY]
110
+
111
+ if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
112
+ session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
113
+ session_data = marshal(session_data.to_hash)
114
+
115
+ raise CookieOverflow if session_data.size > MAX
116
+
117
+ cookie = Hash.new
118
+ cookie[:value] = session_data
119
+ unless options[:expire_after].nil?
120
+ cookie[:expires] = Time.now + options[:expire_after]
121
+ end
122
+
123
+ cookie = build_cookie(@key, cookie.merge(options))
124
+ unless headers[HTTP_SET_COOKIE].blank?
125
+ headers[HTTP_SET_COOKIE] << "\n#{cookie}"
126
+ else
127
+ headers[HTTP_SET_COOKIE] = cookie
128
+ end
129
+ end
130
+
131
+ [status, headers, body]
132
+ end
133
+
134
+ private
135
+ # Should be in Rack::Utils soon
136
+ def build_cookie(key, value)
137
+ case value
138
+ when Hash
139
+ domain = "; domain=" + value[:domain] if value[:domain]
140
+ path = "; path=" + value[:path] if value[:path]
141
+ # According to RFC 2109, we need dashes here.
142
+ # N.B.: cgi.rb uses spaces...
143
+ expires = "; expires=" + value[:expires].clone.gmtime.
144
+ strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
145
+ secure = "; secure" if value[:secure]
146
+ httponly = "; HttpOnly" if value[:httponly]
147
+ value = value[:value]
148
+ end
149
+ value = [value] unless Array === value
150
+ cookie = Rack::Utils.escape(key) + "=" +
151
+ value.map { |v| Rack::Utils.escape(v) }.join("&") +
152
+ "#{domain}#{path}#{expires}#{secure}#{httponly}"
153
+ end
154
+
155
+ def load_session(env)
156
+ request = Rack::Request.new(env)
157
+ session_data = request.cookies[@key]
158
+ data = unmarshal(session_data) || persistent_session_id!({})
159
+ data.stringify_keys!
160
+ [data["session_id"], data]
161
+ end
162
+
163
+ # Marshal a session hash into safe cookie data. Include an integrity hash.
164
+ def marshal(session)
165
+ @verifier.generate(persistent_session_id!(session))
166
+ end
167
+
168
+ # Unmarshal cookie data to a hash and verify its integrity.
169
+ def unmarshal(cookie)
170
+ persistent_session_id!(@verifier.verify(cookie)) if cookie
171
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
172
+ nil
173
+ end
174
+
175
+ def ensure_session_key(key)
176
+ if key.blank?
177
+ raise ArgumentError, 'A key is required to write a ' +
178
+ 'cookie containing the session data. Use ' +
179
+ 'config.action_controller.session = { :key => ' +
180
+ '"_myapp_session", :secret => "some secret phrase" } in ' +
181
+ 'config/environment.rb'
182
+ end
183
+ end
184
+
185
+ # To prevent users from using something insecure like "Password" we make sure that the
186
+ # secret they've provided is at least 30 characters in length.
187
+ def ensure_secret_secure(secret)
188
+ # There's no way we can do this check if they've provided a proc for the
189
+ # secret.
190
+ return true if secret.is_a?(Proc)
191
+
192
+ if secret.blank?
193
+ raise ArgumentError, "A secret is required to generate an " +
194
+ "integrity hash for cookie session data. Use " +
195
+ "config.action_controller.session = { :key => " +
196
+ "\"_myapp_session\", :secret => \"some secret phrase of at " +
197
+ "least #{SECRET_MIN_LENGTH} characters\" } " +
198
+ "in config/environment.rb"
199
+ end
200
+
201
+ if secret.length < SECRET_MIN_LENGTH
202
+ raise ArgumentError, "Secret should be something secure, " +
203
+ "like \"#{ActiveSupport::SecureRandom.hex(16)}\". The value you " +
204
+ "provided, \"#{secret}\", is shorter than the minimum length " +
205
+ "of #{SECRET_MIN_LENGTH} characters"
206
+ end
207
+ end
208
+
209
+ def verifier_for(secret, digest)
210
+ key = secret.respond_to?(:call) ? secret.call : secret
211
+ ActiveSupport::MessageVerifier.new(key, digest)
212
+ end
213
+
214
+ def generate_sid
215
+ ActiveSupport::SecureRandom.hex(16)
216
+ end
217
+
218
+ def persistent_session_id!(data)
219
+ (data ||= {}).merge!(inject_persistent_session_id(data))
220
+ end
221
+
222
+ def inject_persistent_session_id(data)
223
+ requires_session_id?(data) ? { "session_id" => generate_sid } : {}
224
+ end
225
+
226
+ def requires_session_id?(data)
227
+ if data
228
+ data.respond_to?(:key?) && !data.key?("session_id")
229
+ else
230
+ true
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end