public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Switch to Rack based session stores.
josh (author)
Mon Dec 15 14:33:31 -0800 2008
commit  ed708307137c811d14e5fd2cb4ea550add381a82
tree    31cb7df0a489bb4bbb0a9bc9edb24a70a869a0d1
parent  e8c1915416579a3840573ca2c80822d96cb31823
...
89
90
91
92
93
94
95
96
97
98
99
 
 
100
101
102
103
 
 
 
 
104
105
106
...
89
90
91
 
 
 
 
 
 
 
 
92
93
94
 
95
96
97
98
99
100
101
102
103
0
@@ -89,18 +89,15 @@ module ActionController
0
     autoload :Headers, 'action_controller/headers'
0
   end
0
 
0
-  # DEPRECATE: Remove CGI support
0
-  autoload :CgiRequest, 'action_controller/cgi_process'
0
-  autoload :CGIHandler, 'action_controller/cgi_process'
0
-end
0
-
0
-class CGI
0
-  class Session
0
-    autoload :ActiveRecordStore, 'action_controller/session/active_record_store'
0
+  module Session
0
+    autoload :AbstractStore, 'action_controller/session/abstract_store'
0
     autoload :CookieStore, 'action_controller/session/cookie_store'
0
-    autoload :DRbStore, 'action_controller/session/drb_store'
0
     autoload :MemCacheStore, 'action_controller/session/mem_cache_store'
0
   end
0
+
0
+  # DEPRECATE: Remove CGI support
0
+  autoload :CgiRequest, 'action_controller/cgi_process'
0
+  autoload :CGIHandler, 'action_controller/cgi_process'
0
 end
0
 
0
 autoload :Mime, 'action_controller/mime_type'
...
164
165
166
167
168
 
 
169
170
171
...
1216
1217
1218
1219
1220
1221
1222
...
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
...
164
165
166
 
 
167
168
169
170
171
...
1216
1217
1218
 
1219
1220
1221
...
1228
1229
1230
 
 
 
 
 
 
 
1231
1232
1233
0
@@ -164,8 +164,8 @@ module ActionController #:nodoc:
0
   #
0
   # Other options for session storage are:
0
   #
0
-  # * ActiveRecordStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
0
-  #   unlike CookieStore, hides your session contents from the user. To use ActiveRecordStore, set
0
+  # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
0
+  #   unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
0
   #
0
   #     config.action_controller.session_store = :active_record_store
0
   #
0
@@ -1216,7 +1216,6 @@ module ActionController #:nodoc:
0
       def log_processing
0
         if logger && logger.info?
0
           log_processing_for_request_id
0
-          log_processing_for_session_id
0
           log_processing_for_parameters
0
         end
0
       end
0
@@ -1229,13 +1228,6 @@ module ActionController #:nodoc:
0
         logger.info(request_id)
0
       end
0
 
0
-      def log_processing_for_session_id
0
-        if @_session && @_session.respond_to?(:session_id) && @_session.respond_to?(:dbman) &&
0
-            !@_session.dbman.is_a?(CGI::Session::CookieStore)
0
-          logger.info "  Session ID: #{@_session.session_id}"
0
-        end
0
-      end
0
-
0
       def log_processing_for_parameters
0
         parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
0
         parameters = parameters.except!(:controller, :action, :format, :_method)
...
1
2
3
4
5
6
7
...
1
2
3
 
4
5
6
0
@@ -1,7 +1,6 @@
0
 require 'action_controller/cgi_ext/stdinput'
0
 require 'action_controller/cgi_ext/query_extension'
0
 require 'action_controller/cgi_ext/cookie'
0
-require 'action_controller/cgi_ext/session'
0
 
0
 class CGI #:nodoc:
0
   include ActionController::CgiExt::Stdinput
...
61
62
63
64
 
65
66
67
...
61
62
63
 
64
65
66
67
0
@@ -61,7 +61,7 @@ module ActionController #:nodoc:
0
 
0
   class CgiRequest #:nodoc:
0
     DEFAULT_SESSION_OPTIONS = {
0
-      :database_manager  => CGI::Session::CookieStore,
0
+      :database_manager  => nil,
0
       :prefix            => "ruby_sess.",
0
       :session_path      => "/",
0
       :session_key       => "_session_id",
...
45
46
47
48
49
 
 
 
 
50
51
52
...
89
90
91
92
 
93
94
95
...
45
46
47
 
 
48
49
50
51
52
53
54
...
91
92
93
 
94
95
96
97
0
@@ -45,8 +45,10 @@ module ActionController
0
     end
0
 
0
     cattr_accessor :middleware
0
-    self.middleware = MiddlewareStack.new
0
-    self.middleware.use "ActionController::Failsafe"
0
+    self.middleware = MiddlewareStack.new do |middleware|
0
+      middleware.use "ActionController::Failsafe"
0
+      middleware.use "ActionController::SessionManagement::Middleware"
0
+    end
0
 
0
     include ActiveSupport::Callbacks
0
     define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
0
@@ -89,7 +91,7 @@ module ActionController
0
 
0
     def _call(env)
0
       @request = RackRequest.new(env)
0
-      @response = RackResponse.new(@request)
0
+      @response = RackResponse.new
0
       dispatch
0
     end
0
 
...
489
490
491
492
493
 
 
494
495
496
...
489
490
491
 
 
492
493
494
495
496
0
@@ -489,8 +489,8 @@ EOF
0
       # By default, a single session is automatically created for you, but you
0
       # can use this method to open multiple sessions that ought to be tested
0
       # simultaneously.
0
-      def open_session
0
-        application = ActionController::Dispatcher.new
0
+      def open_session(application = nil)
0
+        application ||= ActionController::Dispatcher.new
0
         session = Integration::Session.new(application)
0
 
0
         # delegate the fixture accessors back to the test instance
...
4
5
6
7
 
 
 
 
 
 
8
9
10
...
21
22
23
24
25
 
 
26
27
28
29
30
 
 
 
 
 
31
32
33
 
 
 
 
 
34
35
 
 
36
37
38
...
4
5
6
 
7
8
9
10
11
12
13
14
15
...
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
0
@@ -4,7 +4,12 @@ module ActionController
0
       attr_reader :klass, :args, :block
0
 
0
       def initialize(klass, *args, &block)
0
-        @klass = klass.is_a?(Class) ? klass : klass.to_s.constantize
0
+        if klass.is_a?(Class)
0
+          @klass = klass
0
+        else
0
+          @klass = klass.to_s.constantize
0
+        end
0
+
0
         @args = args
0
         @block = block
0
       end
0
@@ -21,18 +26,28 @@ module ActionController
0
       end
0
 
0
       def inspect
0
-        str = @klass.to_s
0
-        @args.each { |arg| str += ", #{arg.inspect}" }
0
+        str = klass.to_s
0
+        args.each { |arg| str += ", #{arg.inspect}" }
0
         str
0
       end
0
 
0
       def build(app)
0
-        klass.new(app, *args, &block)
0
+        if block
0
+          klass.new(app, *args, &block)
0
+        else
0
+          klass.new(app, *args)
0
+        end
0
       end
0
     end
0
 
0
+    def initialize(*args, &block)
0
+      super(*args)
0
+      block.call(self) if block_given?
0
+    end
0
+
0
     def use(*args, &block)
0
-      push(Middleware.new(*args, &block))
0
+      middleware = Middleware.new(*args, &block)
0
+      push(middleware)
0
     end
0
 
0
     def build(app)
...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
22
23
24
25
26
...
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
129
130
131
132
133
134
135
136
137
138
139
140
 
 
 
141
142
143
144
 
 
 
145
146
147
148
149
 
150
151
152
...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
...
3
4
5
 
6
7
8
9
 
 
 
 
 
 
 
 
 
 
 
10
11
 
12
13
14
...
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
...
173
174
175
 
 
176
177
178
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
0
@@ -3,24 +3,12 @@ require 'action_controller/cgi_ext'
0
 module ActionController #:nodoc:
0
   class RackRequest < AbstractRequest #:nodoc:
0
     attr_accessor :session_options
0
-    attr_reader :cgi
0
 
0
     class SessionFixationAttempt < StandardError #:nodoc:
0
     end
0
 
0
-    DEFAULT_SESSION_OPTIONS = {
0
-      :database_manager => CGI::Session::CookieStore, # store data in cookie
0
-      :prefix           => "ruby_sess.",    # prefix session file names
0
-      :session_path     => "/",             # available to all paths in app
0
-      :session_key      => "_session_id",
0
-      :cookie_only      => true,
0
-      :session_http_only=> true
0
-    }
0
-
0
-    def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
0
-      @session_options = session_options
0
+    def initialize(env)
0
       @env = env
0
-      @cgi = CGIWrapper.new(self)
0
       super()
0
     end
0
 
0
@@ -66,87 +54,25 @@ module ActionController #:nodoc:
0
       @env['SERVER_SOFTWARE'].split("/").first
0
     end
0
 
0
-    def session
0
-      unless defined?(@session)
0
-        if @session_options == false
0
-          @session = Hash.new
0
-        else
0
-          stale_session_check! do
0
-            if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
0
-              raise SessionFixationAttempt
0
-            end
0
-            case value = session_options_with_string_keys['new_session']
0
-              when true
0
-                @session = new_session
0
-              when false
0
-                begin
0
-                  @session = CGI::Session.new(@cgi, session_options_with_string_keys)
0
-                # CGI::Session raises ArgumentError if 'new_session' == false
0
-                # and no session cookie or query param is present.
0
-                rescue ArgumentError
0
-                  @session = Hash.new
0
-                end
0
-              when nil
0
-                @session = CGI::Session.new(@cgi, session_options_with_string_keys)
0
-              else
0
-                raise ArgumentError, "Invalid new_session option: #{value}"
0
-            end
0
-            @session['__valid_session']
0
-          end
0
-        end
0
-      end
0
-      @session
0
+    def session_options
0
+      @env['rack.session.options'] ||= {}
0
     end
0
 
0
-    def reset_session
0
-      @session.delete if defined?(@session) && @session.is_a?(CGI::Session)
0
-      @session = new_session
0
+    def session_options=(options)
0
+      @env['rack.session.options'] = options
0
     end
0
 
0
-    private
0
-      # Delete an old session if it exists then create a new one.
0
-      def new_session
0
-        if @session_options == false
0
-          Hash.new
0
-        else
0
-          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
0
-          CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
0
-        end
0
-      end
0
-
0
-      def cookie_only?
0
-        session_options_with_string_keys['cookie_only']
0
-      end
0
-
0
-      def stale_session_check!
0
-        yield
0
-      rescue ArgumentError => argument_error
0
-        if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
0
-          begin
0
-            # Note that the regexp does not allow $1 to end with a ':'
0
-            $1.constantize
0
-          rescue LoadError, NameError => const_error
0
-            raise ActionController::SessionRestoreError, <<-end_msg
0
-Session contains objects whose class definition isn\'t available.
0
-Remember to require the classes for all objects kept in the session.
0
-(Original exception: #{const_error.message} [#{const_error.class}])
0
-end_msg
0
-          end
0
-
0
-          retry
0
-        else
0
-          raise
0
-        end
0
-      end
0
+    def session
0
+      @env['rack.session'] ||= {}
0
+    end
0
 
0
-      def session_options_with_string_keys
0
-        @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
0
-      end
0
+    def reset_session
0
+      @env['rack.session'] = {}
0
+    end
0
   end
0
 
0
   class RackResponse < AbstractResponse #:nodoc:
0
-    def initialize(request)
0
-      @cgi = request.cgi
0
+    def initialize
0
       @writer = lambda { |x| @body << x }
0
       @block = nil
0
       super()
0
@@ -247,49 +173,8 @@ end_msg
0
             else            cookies << cookie.to_s
0
           end
0
 
0
-          @cgi.output_cookies.each { |c| cookies << c.to_s } if @cgi.output_cookies
0
-
0
           headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact
0
         end
0
       end
0
   end
0
-
0
-  class CGIWrapper < ::CGI
0
-    attr_reader :output_cookies
0
-
0
-    def initialize(request, *args)
0
-      @request  = request
0
-      @args     = *args
0
-      @input    = request.body
0
-
0
-      super *args
0
-    end
0
-
0
-    def params
0
-      @params ||= @request.params
0
-    end
0
-
0
-    def cookies
0
-      @request.cookies
0
-    end
0
-
0
-    def query_string
0
-      @request.query_string
0
-    end
0
-
0
-    # Used to wrap the normal args variable used inside CGI.
0
-    def args
0
-      @args
0
-    end
0
-
0
-    # Used to wrap the normal env_table variable used inside CGI.
0
-    def env_table
0
-      @request.env
0
-    end
0
-
0
-    # Used to wrap the normal stdinput variable used inside CGI.
0
-    def stdinput
0
-      @input
0
-    end
0
-  end
0
 end
...
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
129
130
131
132
 
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
 
163
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
129
130
131
132
133
134
135
136
 
 
 
 
 
 
 
 
 
 
137
138
 
 
 
 
 
 
 
 
 
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
0
@@ -1,163 +1,219 @@
0
-require 'cgi'
0
-require 'cgi/session'
0
-
0
-# This cookie-based session store is the Rails default. Sessions typically
0
-# contain at most a user_id and flash message; both fit within the 4K cookie
0
-# size limit. Cookie-based sessions are dramatically faster than the
0
-# alternatives.
0
-#
0
-# If you have more than 4K of session data or don't want your data to be
0
-# visible to the user, pick another session store.
0
-#
0
-# CookieOverflow is raised if you attempt to store more than 4K of data.
0
-# TamperedWithCookie is raised if the data integrity check fails.
0
-#
0
-# A message digest is included with the cookie to ensure data integrity:
0
-# a user cannot alter his +user_id+ without knowing the secret key included in
0
-# the hash. New apps are generated with a pregenerated secret in
0
-# config/environment.rb. Set your own for old apps you're upgrading.
0
-#
0
-# Session options:
0
-#
0
-# * <tt>:secret</tt>: An application-wide key string or block returning a string
0
-#   called per generated digest. The block is called with the CGI::Session
0
-#   instance as an argument. It's important that the secret is not vulnerable to
0
-#   a dictionary attack. Therefore, you should choose a secret consisting of
0
-#   random numbers and letters and more than 30 characters. Examples:
0
-#
0
-#     :secret => '449fe2e7daee471bffae2fd8dc02313d'
0
-#     :secret => Proc.new { User.current_user.secret_key }
0
-#
0
-# * <tt>:digest</tt>: The message digest algorithm used to verify session
0
-#   integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
0
-#   such as 'MD5', 'RIPEMD160', 'SHA256', etc.
0
-#
0
-# To generate a secret key for an existing application, run
0
-# "rake secret" and set the key in config/environment.rb.
0
-#
0
-# Note that changing digest or secret invalidates all existing sessions!
0
-class CGI::Session::CookieStore
0
-  # Cookies can typically store 4096 bytes.
0
-  MAX = 4096
0
-  SECRET_MIN_LENGTH = 30 # characters
0
-
0
-  # Raised when storing more than 4K of session data.
0
-  class CookieOverflow < StandardError; end
0
-
0
-  # Raised when the cookie fails its integrity check.
0
-  class TamperedWithCookie < StandardError; end
0
-
0
-  # Called from CGI::Session only.
0
-  def initialize(session, options = {})
0
-    # The session_key option is required.
0
-    if options['session_key'].blank?
0
-      raise ArgumentError, 'A session_key is required to write a cookie containing the session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase" } in config/environment.rb'
0
-    end
0
+module ActionController
0
+  module Session
0
+    # This cookie-based session store is the Rails default. Sessions typically
0
+    # contain at most a user_id and flash message; both fit within the 4K cookie
0
+    # size limit. Cookie-based sessions are dramatically faster than the
0
+    # alternatives.
0
+    #
0
+    # If you have more than 4K of session data or don't want your data to be
0
+    # visible to the user, pick another session store.
0
+    #
0
+    # CookieOverflow is raised if you attempt to store more than 4K of data.
0
+    #
0
+    # A message digest is included with the cookie to ensure data integrity:
0
+    # a user cannot alter his +user_id+ without knowing the secret key
0
+    # included in the hash. New apps are generated with a pregenerated secret
0
+    # in config/environment.rb. Set your own for old apps you're upgrading.
0
+    #
0
+    # Session options:
0
+    #
0
+    # * <tt>:secret</tt>: An application-wide key string or block returning a
0
+    #   string called per generated digest. The block is called with the
0
+    #   CGI::Session instance as an argument. It's important that the secret
0
+    #   is not vulnerable to a dictionary attack. Therefore, you should choose
0
+    #   a secret consisting of random numbers and letters and more than 30
0
+    #   characters. Examples:
0
+    #
0
+    #     :secret => '449fe2e7daee471bffae2fd8dc02313d'
0
+    #     :secret => Proc.new { User.current_user.secret_key }
0
+    #
0
+    # * <tt>:digest</tt>: The message digest algorithm used to verify session
0
+    #   integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
0
+    #   such as 'MD5', 'RIPEMD160', 'SHA256', etc.
0
+    #
0
+    # To generate a secret key for an existing application, run
0
+    # "rake secret" and set the key in config/environment.rb.
0
+    #
0
+    # Note that changing digest or secret invalidates all existing sessions!
0
+    class CookieStore
0
+      # Cookies can typically store 4096 bytes.
0
+      MAX = 4096
0
+      SECRET_MIN_LENGTH = 30 # characters
0
+
0
+      DEFAULT_OPTIONS = {
0
+        :domain => nil,
0
+        :path => "/",
0
+        :expire_after => nil
0
+      }.freeze
0
+
0
+      ENV_SESSION_KEY = "rack.session".freeze
0
+      ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
0
+      HTTP_SET_COOKIE = "Set-Cookie".freeze
0
+
0
+      # Raised when storing more than 4K of session data.
0
+      class CookieOverflow < StandardError; end
0
+
0
+      def initialize(app, options = {})
0
+        options = options.dup
0
+
0
+        @app = app
0
+
0
+        # The session_key option is required.
0
+        ensure_session_key(options[:key])
0
+        @key = options.delete(:key).freeze
0
+
0
+        # The secret option is required.
0
+        ensure_secret_secure(options[:secret])
0
+        @secret = options.delete(:secret).freeze
0
+
0
+        @digest = options.delete(:digest) || 'SHA1'
0
+        @verifier = verifier_for(@secret, @digest)
0
+
0
+        @default_options = DEFAULT_OPTIONS.merge(options).freeze
0
+
0
+        freeze
0
+      end
0
 
0
-    # The secret option is required.
0
-    ensure_secret_secure(options['secret'])
0
-
0
-    # Keep the session and its secret on hand so we can read and write cookies.
0
-    @session, @secret = session, options['secret']
0
-
0
-    # Message digest defaults to SHA1.
0
-    @digest = options['digest'] || 'SHA1'
0
-
0
-    # Default cookie options derived from session settings.
0
-    @cookie_options = {
0
-      'name'    => options['session_key'],
0
-      'path'    => options['session_path'],
0
-      'domain'  => options['session_domain'],
0
-      'expires' => options['session_expires'],
0
-      'secure'  => options['session_secure'],
0
-      'http_only' => options['session_http_only']
0
-    }
0
-
0
-    # Set no_hidden and no_cookies since the session id is unused and we
0
-    # set our own data cookie.
0
-    options['no_hidden'] = true
0
-    options['no_cookies'] = true
0
-  end
0
+      class SessionHash < Hash
0
+        def initialize(middleware, env)
0
+          @middleware = middleware
0
+          @env = env
0
+          @loaded = false
0
+        end
0
+
0
+        def [](key)
0
+          load! unless @loaded
0
+          super
0
+        end
0
+
0
+        def []=(key, value)
0
+          load! unless @loaded
0
+          super
0
+        end
0
+
0
+        def to_hash
0
+          {}.replace(self)
0
+        end
0
+
0
+        private
0
+          def load!
0
+            replace(@middleware.send(:load_session, @env))
0
+            @loaded = true
0
+          end
0
+      end
0
 
0
-  # To prevent users from using something insecure like "Password" we make sure that the
0
-  # secret they've provided is at least 30 characters in length.
0
-  def ensure_secret_secure(secret)
0
-    # There's no way we can do this check if they've provided a proc for the
0
-    # secret.
0
-    return true if secret.is_a?(Proc)
0
+      def call(env)
0
+        session_data = SessionHash.new(self, env)
0
+        original_value = session_data.dup
0
 
0
-    if secret.blank?
0
-      raise ArgumentError, %Q{A secret is required to generate an integrity hash for cookie session data. Use config.action_controller.session = { :session_key => "_myapp_session", :secret => "some secret phrase of at least #{SECRET_MIN_LENGTH} characters" } in config/environment.rb}
0
-    end
0
+        env[ENV_SESSION_KEY] = session_data
0
+        env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
0
 
0
-    if secret.length < SECRET_MIN_LENGTH
0
-      raise ArgumentError, %Q{Secret should be something secure, like "#{CGI::Session.generate_unique_id}".  The value you provided, "#{secret}", is shorter than the minimum length of #{SECRET_MIN_LENGTH} characters}
0
-    end
0
-  end
0
+        status, headers, body = @app.call(env)
0
 
0
-  # Restore session data from the cookie.
0
-  def restore
0
-    @original = read_cookie
0
-    @data = unmarshal(@original) || {}
0
-  end
0
+        unless env[ENV_SESSION_KEY] == original_value
0
+          session_data = marshal(env[ENV_SESSION_KEY].to_hash)
0
 
0
-  # Wait until close to write the session data cookie.
0
-  def update; end
0
+          raise CookieOverflow if session_data.size > MAX
0
 
0
-  # Write the session data cookie if it was loaded and has changed.
0
-  def close
0
-    if defined?(@data) && !@data.blank?
0
-      updated = marshal(@data)
0
-      raise CookieOverflow if updated.size > MAX
0
-      write_cookie('value' => updated) unless updated == @original
0
-    end
0
-  end
0
+          options = env[ENV_SESSION_OPTIONS_KEY]
0
+          cookie = Hash.new
0
+          cookie[:value] = session_data
0
+          unless options[:expire_after].nil?
0
+            cookie[:expires] = Time.now + options[:expire_after]
0
+          end
0
 
0
-  # Delete the session data by setting an expired cookie with no data.
0
-  def delete
0
-    @data = nil
0
-    clear_old_cookie_value
0
-    write_cookie('value' => nil, 'expires' => 1.year.ago)
0
-  end
0
+          cookie = build_cookie(@key, cookie.merge(options))
0
+          case headers[HTTP_SET_COOKIE]
0
+          when Array
0
+            headers[HTTP_SET_COOKIE] << cookie
0
+          when String
0
+            headers[HTTP_SET_COOKIE] = [headers[HTTP_SET_COOKIE], cookie]
0
+          when nil
0
+            headers[HTTP_SET_COOKIE] = cookie
0
+          end
0
+        end
0
 
0
-  private
0
-    # Marshal a session hash into safe cookie data. Include an integrity hash.
0
-    def marshal(session)
0
-      verifier.generate(session)
0
-    end
0
-
0
-    # Unmarshal cookie data to a hash and verify its integrity.
0
-    def unmarshal(cookie)
0
-      if cookie
0
-        verifier.verify(cookie)
0
+        [status, headers, body]
0
       end
0
-    rescue ActiveSupport::MessageVerifier::InvalidSignature
0
-      delete
0
-      raise TamperedWithCookie
0
-    end
0
-
0
-    # Read the session data cookie.
0
-    def read_cookie
0
-      @session.cgi.cookies[@cookie_options['name']].first
0
-    end
0
 
0
-    # CGI likes to make you hack.
0
-    def write_cookie(options)
0
-      cookie = CGI::Cookie.new(@cookie_options.merge(options))
0
-      @session.cgi.send :instance_variable_set, '@output_cookies', [cookie]
0
-    end
0
-
0
-    # Clear cookie value so subsequent new_session doesn't reload old data.
0
-    def clear_old_cookie_value
0
-      @session.cgi.cookies[@cookie_options['name']].clear
0
-    end
0
-    
0
-    def verifier
0
-      if @secret.respond_to?(:call)
0
-        key = @secret.call
0
-      else
0
-        key = @secret
0
-      end
0
-      ActiveSupport::MessageVerifier.new(key, @digest)
0
+      private
0
+        # Should be in Rack::Utils soon
0
+        def build_cookie(key, value)
0
+          case value
0
+          when Hash
0
+            domain  = "; domain="  + value[:domain] if value[:domain]
0
+            path    = "; path="    + value[:path]   if value[:path]
0
+            # According to RFC 2109, we need dashes here.
0
+            # N.B.: cgi.rb uses spaces...
0
+            expires = "; expires=" + value[:expires].clone.gmtime.
0
+              strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
0
+            secure = "; secure" if value[:secure]
0
+            httponly = "; httponly" if value[:httponly]
0
+            value = value[:value]
0
+          end
0
+          value = [value] unless Array === value
0
+          cookie = Rack::Utils.escape(key) + "=" +
0
+            value.map { |v| Rack::Utils.escape(v) }.join("&") +
0
+            "#{domain}#{path}#{expires}#{secure}#{httponly}"
0
+        end
0
+
0
+        def load_session(env)
0
+          request = Rack::Request.new(env)
0
+          session_data = request.cookies[@key]
0
+          unmarshal(session_data) || {}
0
+        end
0
+
0
+        # Marshal a session hash into safe cookie data. Include an integrity hash.
0
+        def marshal(session)
0
+          @verifier.generate(session)
0
+        end
0
+
0
+        # Unmarshal cookie data to a hash and verify its integrity.
0
+        def unmarshal(cookie)
0
+          @verifier.verify(cookie) if cookie
0
+        rescue ActiveSupport::MessageVerifier::InvalidSignature
0
+          nil
0
+        end
0
+
0
+        def ensure_session_key(key)
0
+          if key.blank?
0
+            raise ArgumentError, 'A session_key is required to write a ' +
0
+              'cookie containing the session data. Use ' +
0
+              'config.action_controller.session = { :session_key => ' +
0
+              '"_myapp_session", :secret => "some secret phrase" } in ' +
0
+              'config/environment.rb'
0
+          end
0
+        end
0
+
0
+        # To prevent users from using something insecure like "Password" we make sure that the
0
+        # secret they've provided is at least 30 characters in length.
0
+        def ensure_secret_secure(secret)
0
+          # There's no way we can do this check if they've provided a proc for the
0
+          # secret.
0
+          return true if secret.is_a?(Proc)
0
+
0
+          if secret.blank?
0
+            raise ArgumentError, "A secret is required to generate an " +
0
+              "integrity hash for cookie session data. Use " +
0
+              "config.action_controller.session = { :session_key => " +
0
+              "\"_myapp_session\", :secret => \"some secret phrase of at " +
0
+              "least #{SECRET_MIN_LENGTH} characters\" } " +
0
+              "in config/environment.rb"
0
+          end
0
+
0
+          if secret.length < SECRET_MIN_LENGTH
0
+            raise ArgumentError, "Secret should be something secure, " +
0
+              "like \"#{ActiveSupport::SecureRandom.hex(16)}\".  The value you " +
0
+              "provided, \"#{secret}\", is shorter than the minimum length " +
0
+              "of #{SECRET_MIN_LENGTH} characters"
0
+          end
0
+        end
0
+
0
+        def verifier_for(secret, digest)
0
+          key = secret.respond_to?(:call) ? secret.call : secret
0
+          ActiveSupport::MessageVerifier.new(key, digest)
0
+        end
0
     end
0
+  end
0
 end
...
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
...
 
 
 
 
 
 
 
 
 
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
0
@@ -1,95 +1,48 @@
0
-# cgi/session/memcached.rb - persistent storage of marshalled session data
0
-#
0
-# == Overview
0
-#
0
-# This file provides the CGI::Session::MemCache class, which builds
0
-# persistence of storage data on top of the MemCache library.  See
0
-# cgi/session.rb for more details on session storage managers.
0
-#
0
-
0
 begin
0
-  require 'cgi/session'
0
   require_library_or_gem 'memcache'
0
 
0
-  class CGI
0
-    class Session
0
-      # MemCache-based session storage class.
0
-      #
0
-      # This builds upon the top-level MemCache class provided by the
0
-      # library file memcache.rb.  Session data is marshalled and stored
0
-      # in a memcached cache.
0
-      class MemCacheStore
0
-        def check_id(id) #:nodoc:#
0
-          /[^0-9a-zA-Z]+/ =~ id.to_s ? false : true
0
-        end
0
+  module ActionController
0
+    module Session
0
+      class MemCacheStore < AbstractStore
0
+        def initialize(app, options = {})
0
+          # Support old :expires option
0
+          options[:expire_after] ||= options[:expires]
0
 
0
-        # Create a new CGI::Session::MemCache instance
0
-        #
0
-        # This constructor is used internally by CGI::Session. The
0
-        # user does not generally need to call it directly.
0
-        #
0
-        # +session+ is the session for which this instance is being
0
-        # created. The session id must only contain alphanumeric
0
-        # characters; automatically generated session ids observe
0
-        # this requirement.
0
-        #
0
-        # +options+ is a hash of options for the initializer. The
0
-        # following options are recognized:
0
-        #
0
-        # cache::  an instance of a MemCache client to use as the
0
-        #      session cache.
0
-        #
0
-        # expires:: an expiry time value to use for session entries in
0
-        #     the session cache. +expires+ is interpreted in seconds
0
-        #     relative to the current time if it’s less than 60*60*24*30
0
-        #     (30 days), or as an absolute Unix time (e.g., Time#to_i) if
0
-        #     greater. If +expires+ is +0+, or not passed on +options+,
0
-        #     the entry will never expire.
0
-        #
0
-        # This session's memcache entry will be created if it does
0
-        # not exist, or retrieved if it does.
0
-        def initialize(session, options = {})
0
-          id = session.session_id
0
-          unless check_id(id)
0
-            raise ArgumentError, "session_id '%s' is invalid" % id
0
-          end
0
-          @cache = options['cache'] || MemCache.new('localhost')
0
-          @expires = options['expires'] || 0
0
-          @session_key = "session:#{id}"
0
-          @session_data = {}
0
-          # Add this key to the store if haven't done so yet
0
-          unless @cache.get(@session_key)
0
-            @cache.add(@session_key, @session_data, @expires)
0
-          end
0
-        end
0
+          super
0
 
0
-        # Restore session state from the session's memcache entry.
0
-        #
0
-        # Returns the session state as a hash.
0
-        def restore
0
-          @session_data = @cache[@session_key] || {}
0
-        end
0
+          @default_options = {
0
+            :namespace => 'rack:session',
0
+            :memcache_server => 'localhost:11211'
0
+          }.merge(@default_options)
0
 
0
-        # Save session state to the session's memcache entry.
0
-        def update
0
-          @cache.set(@session_key, @session_data, @expires)
0
-        end
0
-      
0
-        # Update and close the session's memcache entry.
0
-        def close
0
-          update
0
-        end
0
+          @pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options)
0
+          unless @pool.servers.any? { |s| s.alive? }
0
+            raise "#{self} unable to find server during initialization."
0
+          end
0
+          @mutex = Mutex.new
0
 
0
-        # Delete the session's memcache entry.
0
-        def delete
0
-          @cache.delete(@session_key)
0
-          @session_data = {}
0
-        end
0
-        
0
-        def data
0
-          @session_data
0
+          super
0
         end
0
 
0
+        private
0
+          def get_session(env, sid)
0
+            sid ||= generate_sid
0
+            begin
0
+              session = @pool.get(sid) || {}
0
+            rescue MemCache::MemCacheError, Errno::ECONNREFUSED
0
+              session = {}
0
+            end
0
+            [sid, session]
0
+          end
0
+
0
+          def set_session(env, sid, session_data)
0
+            options = env['rack.session.options']
0
+            expiry  = options[:expire_after] || 0
0
+            @pool.set(sid, session_data, expiry)
0
+            return true
0
+          rescue MemCache::MemCacheError, Errno::ECONNREFUSED
0
+            return false
0
+          end
0
       end
0
     end
0
   end
...
3
4
5
6
7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
9
10
...
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
...
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
...
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
0
@@ -3,8 +3,29 @@ module ActionController #:nodoc:
0
     def self.included(base)
0
       base.class_eval do
0
         extend ClassMethods
0
-        alias_method_chain :process, :session_management_support
0
-        alias_method_chain :process_cleanup, :session_management_support
0
+      end
0
+    end
0
+
0
+    class Middleware
0
+      DEFAULT_OPTIONS = {
0
+        :path     => "/",
0
+        :key      => "_session_id",
0
+        :httponly => true,
0
+      }.freeze
0
+
0
+      def self.new(app)
0
+        cgi_options = ActionController::Base.session_options
0
+        options = cgi_options.symbolize_keys
0
+        options = DEFAULT_OPTIONS.merge(options)
0
+        options[:path] = options.delete(:session_path)
0
+        options[:key] = options.delete(:session_key)
0
+        options[:httponly] = options.delete(:session_http_only)
0
+
0
+        if store = ActionController::Base.session_store
0
+          store.new(app, options)
0
+        else # Sessions disabled
0
+          lambda { |env| app.call(env) }
0
+        end
0
       end
0
     end
0
 
0
@@ -12,144 +33,45 @@ module ActionController #:nodoc:
0
       # Set the session store to be used for keeping the session data between requests.
0
       # By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>),
0
       # but you can also specify one of the other included stores (<tt>:active_record_store</tt>,
0
-      # <tt>:p_store</tt>, <tt>:drb_store</tt>, <tt>:mem_cache_store</tt>, or
0
-      # <tt>:memory_store</tt>) or your own custom class.
0
+      # <tt>:mem_cache_store</tt>, or your own custom class.
0
       def session_store=(store)
0
-        ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager] =
0
-          store.is_a?(Symbol) ? CGI::Session.const_get(store == :drb_store ? "DRbStore" : store.to_s.camelize) : store
0
+        if store == :active_record_store
0
+          self.session_store = ActiveRecord::SessionStore
0
+        else
0
+          @@session_store = store.is_a?(Symbol) ?
0
+            Session.const_get(store.to_s.camelize) :
0
+            store
0
+        end
0
       end
0
 
0
       # Returns the session store class currently used.
0
       def session_store
0
-        ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager]
0
+        if defined? @@session_store
0
+          @@session_store
0
+        else
0
+          Session::CookieStore
0
+        end
0
+      end
0
+
0
+      def session=(options = {})
0
+        self.session_store = nil if options.delete(:disabled)
0
+        session_options.merge!(options)
0
       end
0
 
0
       # Returns the hash used to configure the session. Example use:
0
       #
0
       #   ActionController::Base.session_options[:session_secure] = true # session only available over HTTPS
0
       def session_options
0
-        ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
0
-      end
0
-      
0
-      # Specify how sessions ought to be managed for a subset of the actions on
0
-      # the controller. Like filters, you can specify <tt>:only</tt> and
0
-      # <tt>:except</tt> clauses to restrict the subset, otherwise options
0
-      # apply to all actions on this controller.
0
-      #
0
-      # The session options are inheritable, as well, so if you specify them in
0
-      # a parent controller, they apply to controllers that extend the parent.
0
-      #
0
-      # Usage:
0
-      #
0
-      #   # turn off session management for all actions.
0
-      #   session :off
0
-      #
0
-      #   # turn off session management for all actions _except_ foo and bar.
0
-      #   session :off, :except => %w(foo bar)
0
-      #
0
-      #   # turn off session management for only the foo and bar actions.
0
-      #   session :off, :only => %w(foo bar)
0
-      #
0
-      #   # the session will only work over HTTPS, but only for the foo action
0
-      #   session :only => :foo, :session_secure => true
0
-      #
0
-      #   # the session by default uses HttpOnly sessions for security reasons.
0
-      #   # this can be switched off.
0
-      #   session :only => :foo, :session_http_only => false
0
-      #
0
-      #   # the session will only be disabled for 'foo', and only if it is
0
-      #   # requested as a web service
0
-      #   session :off, :only => :foo,
0
-      #           :if => Proc.new { |req| req.parameters[:ws] }
0
-      #
0
-      #   # the session will be disabled for non html/ajax requests
0
-      #   session :off, 
0
-      #     :if => Proc.new { |req| !(req.format.html? || req.format.js?) }
0
-      #
0
-      #   # turn the session back on, useful when it was turned off in the
0
-      #   # application controller, and you need it on in another controller
0
-      #   session :on
0
-      #
0
-      # All session options described for ActionController::Base.process_cgi
0
-      # are valid arguments.
0
-      def session(*args)
0
-        options = args.extract_options!
0
-
0
-        options[:disabled] = false if args.delete(:on)
0
-        options[:disabled] = true if !args.empty?
0
-        options[:only] = [*options[:only]].map { |o| o.to_s } if options[:only]
0
-        options[:except] = [*options[:except]].map { |o| o.to_s } if options[:except]
0
-        if options[:only] && options[:except]
0
-          raise ArgumentError, "only one of either :only or :except are allowed"
0
-        end
0
-
0
-        write_inheritable_array(:session_options, [options])
0
+        @session_options ||= {}
0
       end
0
 
0
-      # So we can declare session options in the Rails initializer.
0
-      alias_method :session=, :session
0
-
0
-      def cached_session_options #:nodoc:
0
-        @session_options ||= read_inheritable_attribute(:session_options) || []
0
-      end
0
-
0
-      def session_options_for(request, action) #:nodoc:
0
-        if (session_options = cached_session_options).empty?
0
-          {}
0
-        else
0
-          options = {}
0
-
0
-          action = action.to_s
0
-          session_options.each do |opts|
0
-            next if opts[:if] && !opts[:if].call(request)
0
-            if opts[:only] && opts[:only].include?(action)
0
-              options.merge!(opts)
0
-            elsif opts[:except] && !opts[:except].include?(action)
0
-              options.merge!(opts)
0
-            elsif !opts[:only] && !opts[:except]
0
-              options.merge!(opts)
0
-            end
0
-          end
0
-          
0
-          if options.empty? then options
0
-          else
0
-            options.delete :only
0
-            options.delete :except
0
-            options.delete :if
0
-            options[:disabled] ? false : options
0
-          end
0
-        end
0
+      def session(*args)
0
+        ActiveSupport::Deprecation.warn(
0
+          "Disabling sessions for a single controller has been deprecated. " +
0
+          "Sessions are now lazy loaded. So if you don't access them, " +
0
+          "consider them off. You can still modify the session cookie " +
0
+          "options with request.session_options.", caller)
0
       end
0
     end
0
-
0
-    def process_with_session_management_support(request, response, method = :perform_action, *arguments) #:nodoc:
0
-      set_session_options(request)
0
-      process_without_session_management_support(request, response, method, *arguments)
0
-    end
0
-
0
-    private
0
-      def set_session_options(request)
0
-        request.session_options = self.class.session_options_for(request, request.parameters["action"] || "index")
0
-      end
0
-      
0
-      def process_cleanup_with_session_management_support
0
-        clear_persistent_model_associations
0
-        process_cleanup_without_session_management_support
0
-      end
0
-
0
-      # Clear cached associations in session data so they don't overflow
0
-      # the database field.  Only applies to ActiveRecordStore since there
0
-      # is not a standard way to iterate over session data.
0
-      def clear_persistent_model_associations #:doc:
0
-        if defined?(@_session) && @_session.respond_to?(:data)
0
-          session_data = @_session.data
0
-
0
-          if session_data && session_data.respond_to?(:each_value)
0
-            session_data.each_value do |obj|
0
-              obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
0
-            end
0
-          end
0
-        end
0
-      end
0
   end
0
 end
...
30
31
32
 
 
33
34
35
...
30
31
32
33
34
35
36
37
0
@@ -30,6 +30,8 @@ ActiveSupport::Deprecation.debug = true
0
 ActionController::Base.logger = nil
0
 ActionController::Routing::Routes.reload rescue nil
0
 
0
+ActionController::Base.session_store = nil
0
+
0
 FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
0
 ActionController::Base.view_paths = FIXTURE_LOAD_PATH
0
 ActionController::Base.view_paths.load
...
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
129
130
131
 
 
 
 
132
133
134
135
136
137
138
139
 
 
 
 
 
 
 
 
 
 
 
140
...
 
 
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
0
@@ -1,140 +1,128 @@
0
-# These tests exercise CGI::Session::ActiveRecordStore, so you're going to
0
-# need AR in a sibling directory to AP and have SQLite installed.
0
 require 'active_record_unit'
0
 
0
-module CommonActiveRecordStoreTests
0
-  def test_basics
0
-    s = session_class.new(:session_id => '1234', :data => { 'foo' => 'bar' })
0
-    assert_equal 'bar', s.data['foo']
0
-    assert s.save
0
-    assert_equal 'bar', s.data['foo']
0
+class ActiveRecordStoreTest < ActionController::IntegrationTest
0
+  DispatcherApp = ActionController::Dispatcher.new
0
+  SessionApp = ActiveRecord::SessionStore.new(DispatcherApp,
0
+                :key => '_session_id')
0
+  SessionAppWithFixation = ActiveRecord::SessionStore.new(DispatcherApp,
0
+                            :key => '_session_id', :cookie_only => false)
0
 
0
-    assert_not_nil t = session_class.find_by_session_id('1234')
0
-    assert_not_nil t.data
0
-    assert_equal 'bar', t.data['foo']
0
-  end
0
-
0
-  def test_reload_same_session
0
-    @new_session.update
0
-    reloaded = CGI::Session.new(CGI.new, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
0
-    assert_equal 'bar', reloaded['foo']
0
-  end
0
-
0
-  def test_tolerates_close_close
0
-    assert_nothing_raised do
0
-      @new_session.close
0
-      @new_session.close
0
+  class TestController < ActionController::Base
0
+    def no_session_access
0
+      head :ok
0
     end
0
-  end
0
-end
0
 
0
-class ActiveRecordStoreTest < ActiveRecordTestCase
0
-  include CommonActiveRecordStoreTests
0
+    def set_session_value
0
+      session[:foo] = params[:foo] || "bar"
0
+      head :ok
0
+    end
0
 
0
-  def session_class
0
-    CGI::Session::ActiveRecordStore::Session
0
-  end
0
+    def get_session_value
0
+      render :text => "foo: #{session[:foo].inspect}"
0
+    end
0
 
0
-  def session_id_column
0
-    "session_id"
0
+    def rescue_action(e) raise end
0
   end
0
 
0
   def setup
0
-    session_class.create_table!
0
-
0
-    ENV['REQUEST_METHOD'] = 'GET'
0
-    ENV['REQUEST_URI'] = '/'
0
-    CGI::Session::ActiveRecordStore.session_class = session_class
0
-
0
-    @cgi = CGI.new
0
-    @new_session = CGI::Session.new(@cgi, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
0
-    @new_session['foo'] = 'bar'
0
+    ActiveRecord::SessionStore.session_class.create_table!
0
+    @integration_session = open_session(SessionApp)
0
   end
0
 
0
-# this test only applies for eager session saving
0
-#  def test_another_instance
0
-#    @another = CGI::Session.new(@cgi, 'session_id' => @new_session.session_id, 'database_manager' => CGI::Session::ActiveRecordStore)
0
-#    assert_equal @new_session.session_id, @another.session_id
0
-#  end
0
-
0
-  def test_model_attribute
0
-    assert_kind_of CGI::Session::ActiveRecordStore::Session, @new_session.model
0
-    assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
0
+  def teardown
0
+    ActiveRecord::SessionStore.session_class.drop_table!
0
   end
0
 
0
-  def test_save_unloaded_session
0
-    c = session_class.connection
0
-    bogus_class = c.quote(ActiveSupport::Base64.encode64("\004\010o:\vBlammo\000"))
0
-    c.insert("INSERT INTO #{session_class.table_name} ('#{session_id_column}', 'data') VALUES ('abcdefghijklmnop', #{bogus_class})")
0
+  def test_setting_and_getting_session_value
0
+    with_test_route_set do
0
+      get '/set_session_value'
0
+      assert_response :success
0
+      assert cookies['_session_id']
0
 
0
-    sess = session_class.find_by_session_id('abcdefghijklmnop')
0
-    assert_not_nil sess
0
-    assert !sess.loaded?
0
+      get '/get_session_value'
0
+      assert_response :success
0
+      assert_equal 'foo: "bar"', response.body
0
 
0
-    # because the session is not loaded, the save should be a no-op. If it
0
-    # isn't, this'll try and unmarshall the bogus class, and should get an error.
0
-    assert_nothing_raised { sess.save }
0
-  end
0
+      get '/set_session_value', :foo => "baz"
0
+      assert_response :success
0
+      assert cookies['_session_id']
0
 
0
-  def teardown
0
-    session_class.drop_table!
0
+      get '/get_session_value'
0
+      assert_response :success
0
+      assert_equal 'foo: "baz"', response.body
0
+    end
0
   end
0
-end
0
 
0
-class ColumnLimitTest < ActiveRecordTestCase
0
-  def setup
0
-    @session_class = CGI::Session::ActiveRecordStore::Session
0
-    @session_class.create_table!
0
+  def test_getting_nil_session_value
0
+    with_test_route_set do
0
+      get '/get_session_value'
0
+      assert_response :success
0
+      assert_equal 'foo: nil', response.body
0
+    end
0
   end
0
 
0
-  def teardown
0
-    @session_class.drop_table!
0
-  end
0
+  def test_prevents_session_fixation
0
+    with_test_route_set do
0
+      get '/set_session_value'
0
+      assert_response :success
0
+      assert cookies['_session_id']
0
 
0
-  def test_protection_from_data_larger_than_column
0
-    # Can't test this unless there is a limit
0
-    return unless limit = @session_class.data_column_size_limit
0
-    too_big = ':(' * limit
0
-    s = @session_class.new(:session_id => '666', :data => {'foo' => too_big})
0
-    s.data
0
-    assert_raise(ActionController::SessionOverflowError) { s.save }
0
-  end
0
-end
0
+      get '/get_session_value'
0
+      assert_response :success
0
+      assert_equal 'foo: "bar"', response.body
0
+      session_id = cookies['_session_id']
0
+      assert session_id
0
+
0
+      reset!
0
 
0
-class DeprecatedActiveRecordStoreTest < ActiveRecordStoreTest
0
-  def session_id_column
0
-    "sessid"
0
+      get '/set_session_value', :_session_id => session_id, :foo => "baz"
0
+      assert_response :success
0
+      assert_equal nil, cookies['_session_id']
0
+
0
+      get '/get_session_value', :_session_id => session_id
0
+      assert_response :success
0
+      assert_equal 'foo: nil', response.body
0
+      assert_equal nil, cookies['_session_id']
0
+    end
0
   end
0
 
0
-  def setup
0
-    session_class.connection.execute 'create table old_sessions (id integer primary key, sessid text unique, data text)'
0
-    session_class.table_name = 'old_sessions'
0
-    session_class.send :setup_sessid_compatibility!
0
+  def test_allows_session_fixation
0
+    @integration_session = open_session(SessionAppWithFixation)
0
 
0
-    ENV['REQUEST_METHOD'] = 'GET'
0
-    CGI::Session::ActiveRecordStore.session_class = session_class
0
+    with_test_route_set do
0
+      get '/set_session_value'
0
+      assert_response :success
0
+      assert cookies['_session_id']
0
 
0
-    @new_session = CGI::Session.new(CGI.new, 'database_manager' => CGI::Session::ActiveRecordStore, 'new_session' => true)
0
-    @new_session['foo'] = 'bar'
0
-  end
0
+      get '/get_session_value'
0
+      assert_response :success
0
+      assert_equal 'foo: "bar"', response.body
0
+      session_id = cookies['_session_id']
0
+      assert session_id
0
 
0
-  def teardown
0
-    session_class.connection.execute 'drop table old_sessions'
0
-    session_class.table_name = 'sessions'
0
-  end
0
-end
0
+      reset!
0
+      @integration_session = open_session(SessionAppWithFixation)
0
+
0
+      get '/set_session_value', :_session_id => session_id, :foo => "baz"
0
+      assert_response :success
0
+      assert_equal session_id, cookies['_session_id']
0
 
0
-class SqlBypassActiveRecordStoreTest < ActiveRecordStoreTest
0
-  def session_class
0
-    unless defined? @session_class
0
-      @session_class = CGI::Session::ActiveRecordStore::SqlBypass
0
-      @session_class.connection = CGI::Session::ActiveRecordStore::Session.connection
0
+      get '/get_session_value', :_session_id => session_id
0
+      assert_response :success
0
+      assert_equal 'foo: "baz"', response.body
0
+      assert_equal session_id, cookies['_session_id']
0
     end
0
-    @session_class
0
   end
0
 
0
-  def test_model_attribute
0
-    assert_kind_of CGI::Session::ActiveRecordStore::SqlBypass, @new_session.model
0
-    assert_equal({ 'foo' => 'bar' }, @new_session.model.data)
0
-  end
0
+  private
0
+    def with_test_route_set
0
+      with_routing do |set|
0
+        set.draw do |map|
0
+          map.with_options :controller => "active_record_store_test/test" do |c|
0
+            c.connect "/:action"
0
+          end
0
+        end
0
+        yield
0
+      end
0
+    end
0
 end
...
231
232
233
234
235
236
237
238
...
231
232
233
 
 
234
235
236
0
@@ -231,8 +231,6 @@ end
0
 
0
 class IntegrationProcessTest < ActionController::IntegrationTest
0
   class IntegrationController < ActionController::Base
0
-    session :off
0
-
0
     def get
0
       respond_to do |format|
0
         format.html { render :text => "OK", :status => 200 }
...
6
7
8
9
10
11
12
13
...
6
7
8
 
 
9
10
11
0
@@ -6,8 +6,6 @@ unless defined? ApplicationController
0
 end
0
 
0
 class UploadTestController < ActionController::Base
0
-  session :off
0
-
0
   def update
0
     SessionUploadTest.last_request_type = ActionController::Base.param_parsers[request.content_type]
0
     render :text => "got here"
...
229
230
231
232
 
233
234
235
...
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
 
296
297
298
...
229
230
231
 
232
233
234
235
...
265
266
267
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
269
270
271
272
 
273
274
275
276
0
@@ -229,7 +229,7 @@ end
0
 class RackResponseTest < BaseRackTest
0
   def setup
0
     super
0
-    @response = ActionController::RackResponse.new(@request)
0
+    @response = ActionController::RackResponse.new
0
   end
0
 
0
   def test_simple_output
0
@@ -265,34 +265,12 @@ class RackResponseTest < BaseRackTest
0
     body.each { |part| parts << part }
0
     assert_equal ["0", "1", "2", "3", "4"], parts
0
   end
0
-
0
-  def test_set_session_cookie
0
-    cookie = CGI::Cookie.new({"name" => "name", "value" => "Josh"})
0
-    @request.cgi.send :instance_variable_set, '@output_cookies', [cookie]
0
-
0
-    @response.body = "Hello, World!"
0
-    @response.prepare!
0
-
0
-    status, headers, body = @response.out
0
-    assert_equal "200 OK", status
0
-    assert_equal({
0
-      "Content-Type" => "text/html; charset=utf-8",
0
-      "Cache-Control" => "private, max-age=0, must-revalidate",
0
-      "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"',
0
-      "Set-Cookie" => ["name=Josh; path="],
0
-      "Content-Length" => "13"
0
-    }, headers)
0
-
0
-    parts = []
0
-    body.each { |part| parts << part }
0
-    assert_equal ["Hello, World!"], parts
0
-  end
0
 end
0
 
0
 class RackResponseHeadersTest < BaseRackTest
0
   def setup
0
     super
0
-    @response = ActionController::RackResponse.new(@request)
0
+    @response = ActionController::RackResponse.new
0
     @response.headers['Status'] = "200 OK"
0
   end
0
 
...
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
 
 
 
 
164
165
166
167
168
169
170
171
172
173
 
 
 
 
 
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
 
 
 
 
 
 
 
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
 
 
 
 
 
 
 
 
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
...
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
129
130
131
132
133
134
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
137
138
139
140
141
142
143
144
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
0
@@ -1,298 +1,146 @@
0
 require 'abstract_unit'
0
 require 'stringio'
0
 
0
+class CookieStoreTest < ActionController::IntegrationTest
0
+  SessionKey = '_myapp_session'
0
+  SessionSecret = 'b3c631c314c0bbca50c1b2843150fe33'
0
 
0
-class CGI::Session::CookieStore
0
-  def ensure_secret_secure_with_test_hax(secret)
0
-    if secret == CookieStoreTest.default_session_options['secret']
0
-      return true
0
-    else
0
-      ensure_secret_secure_without_test_hax(secret)
0
-    end
0
-  end
0
-  alias_method_chain :ensure_secret_secure, :test_hax
0
-end
0
+  DispatcherApp = ActionController::Dispatcher.new
0
+  CookieStoreApp = ActionController::Session::CookieStore.new(DispatcherApp,
0
+                     :key => SessionKey, :secret => SessionSecret)
0
 
0
+  SignedBar = "BAh7BjoIZm9vIghiYXI%3D--" +
0
+    "fef868465920f415f2c0652d6910d3af288a0367"
0
 
0
-# Expose for tests.
0
-class CGI
0
-  attr_reader :output_cookies, :output_hidden
0
-
0
-  class Session
0
-    attr_reader :dbman
0
+  class TestController < ActionController::Base
0
+    def no_session_access
0
+      head :ok
0
+    end
0
 
0
-    class CookieStore
0
-      attr_reader :data, :original, :cookie_options
0
+    def set_session_value
0
+      session[:foo] = "bar"
0
+      head :ok
0
     end
0
-  end
0
-end
0
 
0
-class CookieStoreTest < Test::Unit::TestCase
0
-  def self.default_session_options
0
-    { 'database_manager' => CGI::Session::CookieStore,
0
-      'session_key' => '_myapp_session',
0
-      'secret' => 'Keep it secret; keep it safe.',
0
-      'no_cookies' => true,
0
-      'no_hidden' => true,
0
-      'session_http_only' => true
0
-       }
0
-  end
0
+    def get_session_value
0
+      render :text => "foo: #{session[:foo].inspect}"
0
+    end
0
 
0
-  def self.cookies
0
-    { :empty => ['BAgw--0686dcaccc01040f4bd4f35fe160afe9bc04c330', {}],
0
-      :a_one => ['BAh7BiIGYWkG--5689059497d7f122a7119f171aef81dcfd807fec', { 'a' => 1 }],
0
-      :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--9d20154623b9eeea05c62ab819be0e2483238759', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
0
-      :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--bf9785a666d3c4ac09f7fe3353496b437546cfbf', { 'user_id' => 123, 'flash' => {} }]
0
-    }
0
+    def raise_data_overflow
0
+      session[:foo] = 'bye!' * 1024
0
+      head :ok
0
+    end
0
 
0
+    def rescue_action(e) raise end
0
   end
0
 
0
   def setup
0
-    ENV.delete('HTTP_COOKIE')
0
+    @integration_session = open_session(CookieStoreApp)
0
   end
0
 
0
   def test_raises_argument_error_if_missing_session_key
0
-    [nil, ''].each do |blank|
0
-      assert_raise(ArgumentError, blank.inspect) { new_session 'session_key' => blank }
0
-    end
0
+    assert_raise(ArgumentError, nil.inspect) {
0
+      ActionController::Session::CookieStore.new(nil,
0
+        :key => nil, :secret => SessionSecret)
0
+    }
0
+
0
+    assert_raise(ArgumentError, ''.inspect) {
0
+      ActionController::Session::CookieStore.new(nil,
0
+        :key => '', :secret => SessionSecret)
0
+    }
0
   end
0
 
0
   def test_raises_argument_error_if_missing_secret
0
-    [nil, ''].each do |blank|
0
-      assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
0
-    end
0
-  end
0
+    assert_raise(ArgumentError, nil.inspect) {
0
+      ActionController::Session::CookieStore.new(nil,
0
+       :key => SessionKey, :secret => nil)
0
+    }
0
 
0
-  def test_raises_argument_error_if_secret_is_probably_insecure
0
-    ["password", "secret", "12345678901234567890123456789"].each do |blank|
0
-      assert_raise(ArgumentError, blank.inspect) { new_session 'secret' => blank }
0
-    end
0
+    assert_raise(ArgumentError, ''.inspect) {
0
+      ActionController::Session::CookieStore.new(nil,
0
+       :key => SessionKey, :secret => '')
0
+    }
0
   end
0
 
0
-  def test_reconfigures_session_to_omit_id_cookie_and_hidden_field
0
-    new_session do |session|
0
-      assert_equal true, @options['no_hidden']
0
-      assert_equal true, @options['no_cookies']
0
-    end
0
-  end
0
+  def test_raises_argument_error_if_secret_is_probably_insecure
0
+    assert_raise(ArgumentError, "password".inspect) {
0
+      ActionController::Session::CookieStore.new(nil,
0
+       :key => SessionKey, :secret => "password")
0
+    }
0
 
0
-  def test_restore_unmarshals_missing_cookie_as_empty_hash
0
-    new_session do |session|
0
-      assert_nil session.dbman.data
0
-      assert_nil session['test']
0
-      assert_equal Hash.new, session.dbman.data
0
-    end
0
-  end
0
+    assert_raise(ArgumentError, "secret".inspect) {
0
+      ActionController::Session::CookieStore.new(nil,
0
+       :key => SessionKey, :secret => "secret")
0
+    }
0
 
0
-  def test_restore_unmarshals_good_cookies
0
-    cookies(:empty, :a_one, :typical).each do |value, expected|
0
-      set_cookie! value
0
-      new_session do |session|
0
-        assert_nil session['lazy loads the data hash']
0
-        assert_equal expected, session.dbman.data
0
-      end
0
-    end
0
+    assert_raise(ArgumentError, "12345678901234567890123456789".inspect) {
0
+      ActionController::Session::CookieStore.new(nil,
0
+       :key => SessionKey, :secret => "12345678901234567890123456789")
0
+    }
0
   end
0
 
0
-  def test_restore_deletes_tampered_cookies
0
-    set_cookie! 'a--b'
0
-    new_session do |session|
0
-      assert_raise(CGI::Session::CookieStore::TamperedWithCookie) { session['fail'] }
0
-      assert_cookie_deleted session
0
-    end
0
+  def test_setting_session_value
0
+    with_test_route_set do
0
+      get '/set_session_value'
0
+      assert_response :success
0
+      assert_equal ["_myapp_session=#{SignedBar}; path=/"],
0
+        headers['Set-Cookie']
0
+   end
0
   end
0
 
0
-  def test_close_doesnt_write_cookie_if_data_is_blank
0
-    new_session do |session|
0
-      assert_no_cookies session
0
-      session.close
0
-      assert_no_cookies session
0
-    end
0
+  def test_getting_session_value
0
+    with_test_route_set do
0
+      cookies[SessionKey] = SignedBar
0
+      get '/get_session_value'
0
+      assert_response :success
0
+      assert_equal 'foo: "bar"', response.body
0
+   end
0
   end
0
 
0
-  def test_close_doesnt_write_cookie_if_data_is_unchanged
0
-    set_cookie! cookie_value(:typical)
0
-    new_session do |session|
0
-      assert_no_cookies session
0
-      session['user_id'] = session['user_id']
0
-      session.close
0
-      assert_no_cookies session
0
+  def test_disregards_tampered_sessions
0
+    with_test_route_set do
0
+      cookies[SessionKey] = "BAh7BjoIZm9vIghiYXI%3D--123456780"
0
+      get '/get_session_value'
0
+      assert_response :success
0
+      assert_equal 'foo: nil', response.body
0
     end
0
   end
0
 
0
   def test_close_raises_when_data_overflows
0
-    set_cookie! cookie_value(:empty)
0
-    new_session do |session|
0
-      session['overflow'] = 'bye!' * 1024
0
-      assert_raise(CGI::Session::CookieStore::CookieOverflow) { session.close }
0
-      assert_no_cookies session
0
-    end
0
-  end
0
-
0
-  def test_close_marshals_and_writes_cookie
0
-    set_cookie! cookie_value(:typical)
0
-    new_session do |session|
0
-      assert_no_cookies session
0
-      session['flash'] = {}
0
-      assert_no_cookies session
0
-      session.close
0
-      assert_equal 1, session.cgi.output_cookies.size
0
-      cookie = session.cgi.output_cookies.first
0
-      assert_cookie cookie, cookie_value(:flashed)
0
-      assert_http_only_cookie cookie
0
-      assert_secure_cookie cookie, false
0
-    end
0
-  end
0
-
0
-  def test_writes_non_secure_cookie_by_default
0
-    set_cookie! cookie_value(:typical)
0
-    new_session do |session|
0
-      session['flash'] = {}
0
-      session.close
0
-      cookie = session.cgi.output_cookies.first
0
-      assert_secure_cookie cookie,false
0
-    end
0
-  end
0
-
0
-  def test_writes_secure_cookie
0
-    set_cookie! cookie_value(:typical)
0
-    new_session('session_secure'=>true) do |session|
0
-      session['flash'] = {}
0
-      session.close
0
-      cookie = session.cgi.output_cookies.first
0
-      assert_secure_cookie cookie
0
+    with_test_route_set do
0
+      assert_raise(ActionController::Session::CookieStore::CookieOverflow) {
0
+        get '/raise_data_overflow'
0
+      }
0
     end
0
   end
0
 
0
-  def test_http_only_cookie_by_default
0
-    set_cookie! cookie_value(:typical)
0
-    new_session do |session|
0
-      session['flash'] = {}
0
-      session.close
0
-      cookie = session.cgi.output_cookies.first
0
-      assert_http_only_cookie cookie
0
+  def test_doesnt_write_session_cookie_if_session_is_not_accessed
0
+    with_test_route_set do
0
+      get '/no_session_access'
0
+      assert_response :success
0
+      assert_equal [], headers['Set-Cookie']
0
     end
0
   end
0
 
0
-  def test_overides_http_only_cookie
0
-    set_cookie! cookie_value(:typical)
0
-    new_session('session_http_only'=>false) do |session|
0
-      session['flash'] = {}
0
-      session.close
0
-      cookie = session.cgi.output_cookies.first
0
-      assert_http_only_cookie cookie, false
0
-    end
0
-  end
0
-
0
-  def test_delete_writes_expired_empty_cookie_and_sets_data_to_nil
0
-    set_cookie! cookie_value(:typical)
0
-    new_session do |session|
0
-      assert_no_cookies session
0
-      session.delete
0
-      assert_cookie_deleted session
0
-
0
-      # @data is set to nil so #close doesn't send another cookie.
0
-      session.close
0
-      assert_cookie_deleted session
0
-    end
0
-  end
0
-
0
-  def test_new_session_doesnt_reuse_deleted_cookie_data
0
-    set_cookie! cookie_value(:typical)
0
-
0
-    new_session do |session|
0
-      assert_not_nil session['user_id']
0
-      session.delete
0
-
0
-      # Start a new session using the same CGI instance.
0
-      post_delete_session = CGI::Session.new(session.cgi, self.class.default_session_options)
0
-      assert_nil post_delete_session['user_id']
0
+  def test_doesnt_write_session_cookie_if_session_is_unchanged
0
+    with_test_route_set do
0
+      cookies[SessionKey] = "BAh7BjoIZm9vIghiYXI%3D--" +
0
+        "fef868465920f415f2c0652d6910d3af288a0367"
0
+      get '/no_session_access'
0
+      assert_response :success
0
+      assert_equal [], headers['Set-Cookie']
0
     end
0
   end
0
 
0
   private
0
-    def assert_no_cookies(session)
0
-      assert_nil session.cgi.output_cookies, session.cgi.output_cookies.inspect
0
-    end
0
-
0
-    def assert_cookie_deleted(session, message = 'Expected session deletion cookie to be set')
0
-      assert_equal 1, session.cgi.output_cookies.size
0
-      cookie = session.cgi.output_cookies.first
0
-      assert_cookie cookie, nil, 1.year.ago.to_date, "#{message}: #{cookie.name} => #{cookie.value}"
0
-    end
0
-
0
-    def assert_cookie(cookie, value = nil, expires = nil, message = nil)
0
-      assert_equal '_myapp_session', cookie.name, message
0
-      assert_equal [value].compact, cookie.value, message
0
-      assert_equal expires, cookie.expires ? cookie.expires.to_date : cookie.expires, message
0
-    end
0
-
0
-    def assert_secure_cookie(cookie,value=true)
0
-      assert cookie.secure==value
0
-    end
0
-
0
-    def assert_http_only_cookie(cookie,value=true)
0
-      assert cookie.http_only==value
0
-    end
0
-
0
-    def cookies(*which)
0
-      self.class.cookies.values_at(*which)
0
-    end
0
-
0
-    def cookie_value(which)
0
-      self.class.cookies[which].first
0
-    end
0
-
0
-    def set_cookie!(value)
0
-      ENV['HTTP_COOKIE'] = "_myapp_session=#{value}"
0
-    end
0
-
0
-    def new_session(options = {})
0
-      with_cgi do |cgi|
0
-        assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
0
-        assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
0
-
0
-        @options = self.class.default_session_options.merge(options)
0
-        session = CGI::Session.new(cgi, @options)
0
-        ObjectSpace.undefine_finalizer(session)
0
-
0
-        assert_nil cgi.output_hidden, "Output hidden params should be empty: #{cgi.output_hidden.inspect}"
0
-        assert_nil cgi.output_cookies, "Output cookies should be empty: #{cgi.output_cookies.inspect}"
0
-
0
-        yield session if block_given?
0
-        session
0
+    def with_test_route_set
0
+      with_routing do |set|
0
+        set.draw do |map|
0
+          map.with_options :controller => "cookie_store_test/test" do |c|
0
+            c.connect "/:action"
0
+          end
0
+        end
0
+        yield
0
       end
0
     end
0
-
0
-    def with_cgi
0
-      ENV['REQUEST_METHOD'] = 'GET'
0
-      ENV['HTTP_HOST'] = 'example.com'
0
-      ENV['QUERY_STRING'] = ''
0
-
0
-      cgi = CGI.new('query', StringIO.new(''))
0
-      yield cgi if block_given?
0
-      cgi
0
-    end
0
-end
0
-
0
-
0
-class CookieStoreWithBlockAsSecretTest < CookieStoreTest
0
-  def self.default_session_options
0
-    CookieStoreTest.default_session_options.merge 'secret' => Proc.new { 'Keep it secret; keep it safe.' }
0
-  end
0
-end
0
-
0
-
0
-class CookieStoreWithMD5DigestTest < CookieStoreTest
0
-  def self.default_session_options
0
-    CookieStoreTest.default_session_options.merge 'digest' => 'MD5'
0
-  end
0
-
0
-  def self.cookies
0
-    { :empty => ['BAgw--0415cc0be9579b14afc22ee2d341aa21', {}],
0
-      :a_one => ['BAh7BiIGYWkG--5a0ed962089cc6600ff44168a5d59bc8', { 'a' => 1 }],
0
-      :typical => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7BiILbm90aWNlIgxIZXkgbm93--f426763f6ef435b3738b493600db8d64', { 'user_id' => 123, 'flash' => { 'notice' => 'Hey now' }}],
0
-      :flashed => ['BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA==--0af9156650dab044a53a91a4ddec2c51', { 'user_id' => 123, 'flash' => {} }],
0
-      :double_escaped => [CGI.escape('BAh7ByIMdXNlcl9pZGkBeyIKZmxhc2h7AA%3D%3D--0af9156650dab044a53a91a4ddec2c51'), { 'user_id' => 123, 'flash' => {} }] }
0
-  end
0
 end
...
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
129
130
 
 
 
 
 
131
132
133
 
 
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
 
 
 
 
 
 
 
 
 
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
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
 
 
 
0
@@ -1,178 +1,81 @@
0
 require 'abstract_unit'
0
 
0
-class CGI::Session
0
-  def cache
0
-    dbman.instance_variable_get(:@cache)
0
-  end
0
-end
0
-
0
-
0
-uses_mocha 'MemCacheStore tests' do
0
-if defined? MemCache::MemCacheError
0
+# You need to start a memcached server inorder to run these tests
0
+class MemCacheStoreTest < ActionController::IntegrationTest
0
+  class TestController < ActionController::Base
0
+    def no_session_access
0
+      head :ok
0
+    end
0
 
0
-class MemCacheStoreTest < Test::Unit::TestCase
0
-  SESSION_KEY_RE = /^session:[0-9a-z]+/
0
-  CONN_TEST_KEY = 'connection_test'
0
-  MULTI_TEST_KEY = '0123456789'
0
-  TEST_DATA = 'Hello test'
0
+    def set_session_value
0
+      session[:foo] = "bar"
0
+      head :ok
0
+    end
0
 
0
-  def self.get_mem_cache_if_available
0
-    begin
0
-      require 'memcache'
0
-      cache = MemCache.new('127.0.0.1')
0
-      # Test availability of the connection
0
-      cache.set(CONN_TEST_KEY, 1)
0
-      unless cache.get(CONN_TEST_KEY) == 1
0
-        puts 'Warning: memcache server available but corrupted.'
0
-        return nil
0
-      end
0
-    rescue LoadError, MemCache::MemCacheError
0
-      return nil
0
+    def get_session_value
0
+      render :text => "foo: #{session[:foo].inspect}"
0
     end
0
-    return cache
0
+
0
+    def rescue_action(e) raise end
0
   end
0
 
0
-  CACHE = get_mem_cache_if_available
0
+  begin
0
+    DispatcherApp = ActionController::Dispatcher.new
0
+    MemCacheStoreApp = ActionController::Session::MemCacheStore.new(
0
+                         DispatcherApp, :key => '_session_id')
0
 
0
 
0
-  def test_initialization
0
-    assert_raise(ArgumentError) { new_session('session_id' => '!invalid_id') }
0
-    new_session do |s|
0
-      assert_equal Hash.new, s.cache.get('session:' + s.session_id)
0
+    def setup
0
+      @integration_session = open_session(MemCacheStoreApp)
0
     end
0
-  end
0
 
0
+    def test_setting_and_getting_session_value
0
+      with_test_route_set do
0
+        get '/set_session_value'
0
+        assert_response :success
0
+        assert cookies['_session_id']
0
 
0
-  def test_storage
0
-    d = rand(0xffff)
0
-    new_session do |s|
0
-      session_key = 'session:' + s.session_id
0
-      unless CACHE
0
-        s.cache.expects(:get).with(session_key) \
0
-                             .returns(:test => d)
0
-        s.cache.expects(:set).with(session_key,
0
-                                   has_entry(:test, d),
0
-                                   0)
0
-      end
0
-      s[:test] = d
0
-      s.close
0
-      assert_equal d, s.cache.get(session_key)[:test]
0
-      assert_equal d, s[:test]
0
-    end
0
-  end         
0
-  
0
-  def test_deletion
0
-    new_session do |s|
0
-      session_key = 'session:' + s.session_id
0
-      unless CACHE
0
-        s.cache.expects(:delete)
0
-        s.cache.expects(:get).with(session_key) \
0
-                             .returns(nil)
0
+        get '/get_session_value'
0
+        assert_response :success
0
+        assert_equal 'foo: "bar"', response.body
0
       end
0
-      s[:test] = rand(0xffff)
0
-      s.delete
0
-      assert_nil s.cache.get(session_key)
0
     end
0
-  end
0
 
0
-
0
-  def test_other_session_retrieval
0
-    new_session do |sa|
0
-      unless CACHE
0
-        sa.cache.expects(:set).with('session:' + sa.session_id,
0
-                                    has_entry(:test, TEST_DATA),
0
-                                    0)
0
-      end
0
-      sa[:test] = TEST_DATA
0
-      sa.close
0
-      new_session('session_id' => sa.session_id) do |sb|
0
-        unless CACHE
0
-          sb.cache.expects(:[]).with('session:' + sb.session_id) \
0
-                               .returns(:test => TEST_DATA)
0
-        end
0
-        assert_equal(TEST_DATA, sb[:test])
0
+    def test_getting_nil_session_value
0
+      with_test_route_set do
0
+        get '/get_session_value'
0
+        assert_response :success
0
+      assert_equal 'foo: nil', response.body
0
       end
0
     end
0
-  end
0
 
0
+    def test_prevents_session_fixation
0
+      with_test_route_set do
0
+        get '/get_session_value'
0
+        assert_response :success
0
+        assert_equal 'foo: nil', response.body
0
+        session_id = cookies['_session_id']
0
 
0
-  def test_multiple_sessions
0
-    s_slots = Array.new(10)
0
-    operation = :write
0
-    last_data = nil
0
-    reads = writes = 0
0
-    50.times do
0
-      current = rand(10)
0
-      s_slots[current] ||= new_session('session_id' => MULTI_TEST_KEY,
0
-                                       'new_session' => true)
0
-      s = s_slots[current]
0
-      case operation
0
-      when :write
0
-        last_data = rand(0xffff)
0
-        unless CACHE
0
-          s.cache.expects(:set).with('session:' + MULTI_TEST_KEY,
0
-                                     { :test => last_data },
0
-                                     0)
0
-        end
0
-        s[:test] = last_data
0
-        s.close
0
-        writes += 1
0
-      when :read
0
-        # Make CGI::Session#[] think there was no data retrieval yet.
0
-        # Normally, the session caches the data during its lifetime.
0
-        s.instance_variable_set(:@data, nil)
0
-        unless CACHE
0
-          s.cache.expects(:[]).with('session:' + MULTI_TEST_KEY) \
0
-                              .returns(:test => last_data)
0
-        end
0
-        d = s[:test]
0
-        assert_equal(last_data, d, "OK reads: #{reads}, OK writes: #{writes}")
0
-        reads += 1
0
+        reset!
0
+
0
+        get '/set_session_value', :_session_id => session_id
0
+        assert_response :success
0
+        assert_equal nil, cookies['_session_id']
0
       end
0
-      operation = rand(5) == 0 ? :write : :read
0
     end
0
+  rescue LoadError, RuntimeError
0
+    $stderr.puts "Skipping MemCacheStoreTest tests. Start memcached and try again."
0
   end
0
 
0
-
0
-
0
   private
0
-  def obtain_session_options
0
-    options = { 'database_manager' => CGI::Session::MemCacheStore,
0
-                'session_key' => '_test_app_session'
0
-              }
0
-    # if don't have running memcache server we use mock instead
0
-    unless CACHE
0
-      options['cache'] = c = mock
0
-      c.stubs(:[]).with(regexp_matches(SESSION_KEY_RE))
0
-      c.stubs(:get).with(regexp_matches(SESSION_KEY_RE)) \
0
-                   .returns(Hash.new)
0
-      c.stubs(:add).with(regexp_matches(SESSION_KEY_RE),
0
-                         instance_of(Hash),
0
-                         0)
0
-    end
0
-    options
0
-  end
0
-
0
-
0
-  def new_session(options = {})
0
-    with_cgi do |cgi|
0
-      @options = obtain_session_options.merge(options)
0
-      session = CGI::Session.new(cgi, @options)
0
-      yield session if block_given?
0
-      return session
0
+    def with_test_route_set
0
+      with_routing do |set|
0
+        set.draw do |map|
0
+          map.with_options :controller => "mem_cache_store_test/test" do |c|
0
+            c.connect "/:action"
0
+          end
0
+        end
0
+        yield
0
+      end
0
     end
0
-  end
0
-
0
-  def with_cgi
0
-    ENV['REQUEST_METHOD'] = 'GET'
0
-    ENV['HTTP_HOST'] = 'example.com'
0
-    ENV['QUERY_STRING'] = ''
0
-
0
-    cgi = CGI.new('query', StringIO.new(''))
0
-    yield cgi if block_given?
0
-    cgi
0
-  end
0
 end
0
-
0
-end # defined? MemCache
0
-end # uses_mocha
...
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
0
@@ -1,84 +1,84 @@
0
-require 'abstract_unit'
0
-
0
-class SessionFixationTest < ActionController::IntegrationTest
0
-  class TestController < ActionController::Base
0
-    session :session_key => '_myapp_session_id',
0
-            :secret => CGI::Session.generate_unique_id,
0
-            :except => :default_session_key
0
-
0
-    session :cookie_only => false,
0
-            :only => :allow_session_fixation
0
-
0
-    def default_session_key
0
-      render :text => "default_session_key"
0
-    end
0
-
0
-    def custom_session_key
0
-      render :text => "custom_session_key: #{params[:id]}"
0
-    end
0
-
0
-    def allow_session_fixation
0
-      render :text => "allow_session_fixation"
0
-    end
0
-
0
-    def rescue_action(e) raise end
0
-  end
0
-
0
-  def setup
0
-    @controller = TestController.new
0
-  end
0
-
0
-  def test_should_be_able_to_make_a_successful_request
0
-    with_test_route_set do
0
-      assert_nothing_raised do
0
-        get '/custom_session_key', :id => "1"
0
-      end
0
-      assert_equal 'custom_session_key: 1', @controller.response.body
0
-      assert_not_nil @controller.session
0
-    end
0
-  end
0
-
0
-  def test_should_catch_session_fixation_attempt
0
-    with_test_route_set do
0
-      assert_raises(ActionController::RackRequest::SessionFixationAttempt) do
0
-        get '/custom_session_key', :_myapp_session_id => "42"
0
-      end
0
-      assert_nil @controller.session
0
-    end
0
-  end
0
-
0
-  def test_should_not_catch_session_fixation_attempt_when_cookie_only_setting_is_disabled
0
-    with_test_route_set do
0
-      assert_nothing_raised do
0
-        get '/allow_session_fixation', :_myapp_session_id => "42"
0
-      end
0
-      assert !@controller.response.body.blank?
0
-      assert_not_nil @controller.session
0
-    end
0
-  end
0
-
0
-  def test_should_catch_session_fixation_attempt_with_default_session_key
0
-    # using the default session_key is not possible with cookie store
0
-    ActionController::Base.session_store = :p_store
0
-
0
-    with_test_route_set do
0
-      assert_raises ActionController::RackRequest::SessionFixationAttempt do
0
-        get '/default_session_key', :_session_id => "42"
0
-      end
0
-      assert_nil @controller.response
0
-      assert_nil @controller.session
0
-    end
0
-  end
0
-
0
-  private
0
-    def with_test_route_set
0
-      with_routing do |set|
0
-        set.draw do |map|
0
-          map.with_options :controller => "session_fixation_test/test" do |c|
0
-            c.connect "/:action"
0
-          end
0
-        end
0
-        yield
0
-      end
0
-    end
0
-end
0
+# require 'abstract_unit'
0
+# 
0
+# class SessionFixationTest < ActionController::IntegrationTest
0
+#   class TestController < ActionController::Base
0
+#     session :session_key => '_myapp_session_id',
0
+#             :secret => CGI::Session.generate_unique_id,
0
+#             :except => :default_session_key
0
+# 
0
+#     session :cookie_only => false,
0
+#             :only => :allow_session_fixation
0
+# 
0
+#     def default_session_key
0
+#       render :text => "default_session_key"
0
+#     end
0
+# 
0
+#     def custom_session_key
0
+#       render :text => "custom_session_key: #{params[:id]}"
0
+#     end
0
+# 
0
+#     def allow_session_fixation
0
+#       render :text => "allow_session_fixation"
0
+#     end
0
+# 
0
+#     def rescue_action(e) raise end
0
+#   end
0
+# 
0
+#   def setup
0
+#     @controller = TestController.new
0
+#   end
0
+# 
0
+#   def test_should_be_able_to_make_a_successful_request
0
+#     with_test_route_set do
0
+#       assert_nothing_raised do
0
+#         get '/custom_session_key', :id => "1"
0
+#       end
0
+#       assert_equal 'custom_session_key: 1', @controller.response.body
0
+#       assert_not_nil @controller.session
0
+#     end
0
+#   end
0
+# 
0
+#   def test_should_catch_session_fixation_attempt
0
+#     with_test_route_set do
0
+#       assert_raises(ActionController::RackRequest::SessionFixationAttempt) do
0
+#         get '/custom_session_key', :_myapp_session_id => "42"
0
+#       end
0
+#       assert_nil @controller.session
0
+#     end
0
+#   end
0
+# 
0
+#   def test_should_not_catch_session_fixation_attempt_when_cookie_only_setting_is_disabled
0
+#     with_test_route_set do
0
+#       assert_nothing_raised do
0
+#         get '/allow_session_fixation', :_myapp_session_id => "42"
0
+#       end
0
+#       assert !@controller.response.body.blank?
0
+#       assert_not_nil @controller.session
0
+#     end
0
+#   end
0
+# 
0
+#   def test_should_catch_session_fixation_attempt_with_default_session_key
0
+#     # using the default session_key is not possible with cookie store
0
+#     ActionController::Base.session_store = :p_store
0
+# 
0
+#     with_test_route_set do
0
+#       assert_raises ActionController::RackRequest::SessionFixationAttempt do
0
+#         get '/default_session_key', :_session_id => "42"
0
+#       end
0
+#       assert_nil @controller.response
0
+#       assert_nil @controller.session
0
+#     end
0
+#   end
0
+# 
0
+#   private
0
+#     def with_test_route_set
0
+#       with_routing do |set|
0
+#         set.draw do |map|
0
+#           map.with_options :controller => "session_fixation_test/test" do |c|
0
+#             c.connect "/:action"
0
+#           end
0
+#         end
0
+#         yield
0
+#       end
0
+#     end
0
+# end
...
2
3
4
5
6
7
8
9
...
2
3
4
 
 
5
6
7
0
@@ -2,8 +2,6 @@ require 'abstract_unit'
0
 
0
 class WebServiceTest < ActionController::IntegrationTest
0
   class TestController < ActionController::Base
0
-    session :off
0
-
0
     def assign_parameters
0
       if params[:full]
0
         render :text => dump_params_keys
...
60
61
62
 
63
64
65
...
60
61
62
63
64
65
66
0
@@ -60,6 +60,7 @@ module ActiveRecord
0
   autoload :Schema, 'active_record/schema'
0
   autoload :SchemaDumper, 'active_record/schema_dumper'
0
   autoload :Serialization, 'active_record/serialization'
0
+  autoload :SessionStore, 'active_record/session_store'
0
   autoload :TestCase, 'active_record/test_case'
0
   autoload :Timestamp, 'active_record/timestamp'
0
   autoload :Transactions, 'active_record/transactions'
...
39
40
41
42
 
43
44
45
...
148
149
150
151
152
153
154
...
501
502
503
504
505
506
507
508
509
510
511
512
513
...
529
530
531
532
 
533
534
535
...
39
40
41
 
42
43
44
45
...
148
149
150
 
151
152
153
...
500
501
502
 
 
 
 
 
 
 
503
504
505
...
521
522
523
 
524
525
526
527
0
@@ -39,7 +39,7 @@ module Rails
0
         nil
0
       end
0
     end
0
-    
0
+
0
     def backtrace_cleaner
0
       @@backtrace_cleaner ||= begin
0
         # Relies on ActiveSupport, so we have to lazy load to postpone definition until AS has been loaded
0
@@ -148,7 +148,6 @@ module Rails
0
 
0
       initialize_dependency_mechanism
0
       initialize_whiny_nils
0
-      initialize_temporary_session_directory
0
 
0
       initialize_time_zone
0
       initialize_i18n
0
@@ -501,13 +500,6 @@ Run `rake gems:install` to install the missing gems.
0
       require('active_support/whiny_nil') if configuration.whiny_nils
0
     end
0
 
0
-    def initialize_temporary_session_directory
0
-      if configuration.frameworks.include?(:action_controller)
0
-        session_path = "#{configuration.root_path}/tmp/sessions/"
0
-        ActionController::Base.session_options[:tmpdir] = File.exist?(session_path) ? session_path : Dir::tmpdir
0
-      end
0
-    end
0
-
0
     # Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes.
0
     # If assigned value cannot be matched to a TimeZone, an exception will be raised.
0
     def initialize_time_zone
0
@@ -529,7 +521,7 @@ Run `rake gems:install` to install the missing gems.
0
       end
0
     end
0
 
0
-    # Set the i18n configuration from config.i18n but special-case for the load_path which should be 
0
+    # Set the i18n configuration from config.i18n but special-case for the load_path which should be
0
     # appended to what's already set instead of overwritten.
0
     def initialize_i18n
0
       configuration.i18n.each do |setting, value|
...
380
381
382
383
 
384
385
386
...
380
381
382
 
383
384
385
386
0
@@ -380,7 +380,7 @@ namespace :db do
0
   end
0
 
0
   namespace :sessions do
0
-    desc "Creates a sessions migration for use with CGI::Session::ActiveRecordStore"
0
+    desc "Creates a sessions migration for use with ActiveRecord::SessionStore"
0
     task :create => :environment do
0
       raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
0
       require 'rails_generator'
...
4
5
6
 
7
8
9
...
4
5
6
7
8
9
10
0
@@ -4,6 +4,7 @@ require 'action_controller' # console_app uses 'action_controller/integration'
0
 
0
 unless defined? ApplicationController
0
   class ApplicationController < ActionController::Base; end
0
+  ActionController::Base.session_store = nil
0
 end
0
 
0
 require 'dispatcher'

Comments

methodmissing Wed Dec 17 07:54:39 -0800 2008

Josh,

Not sure if this makes sense, but we’ve been using CookieSessionStore with a persistent session_id for a few months to be able to track features such as visitors to resource X also viewed resource Y etc.

Sole motivation being API compat with other stores that do yield a consistent session identifier minus having to use a server side session store.

I updated it today for compat. with this commit.

http://github.com/methodmissing/stable_session_id/tree/master

Would you consider such a feature useful at the framework level ?

josh Wed Dec 17 08:01:18 -0800 2008

Yes, lets do that. I don’t want to invalidate anyones sessions intentionally.

Please open a ticket on LH and assign it to me.