public
Fork of rails/rails
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/methodmissing/rails.git
Stable session identifier support for CGI::Session::CookieStore
methodmissing (author)
Tue Jul 22 20:45:24 -0700 2008
commit  7f83b1365bcc27939d9b3c308d41f1075eb2670e
tree    8fedafacd2d73c9791112c55f621079da20e7a55
parent  183cc2f23234f5dba8615f26cf67b66b31633b21
...
33
34
35
 
 
 
 
 
36
37
38
...
50
51
52
 
53
54
55
...
59
60
61
62
 
63
64
65
...
129
130
131
 
132
133
134
...
144
145
146
147
 
 
 
148
149
150
 
 
 
 
 
 
 
 
 
 
 
 
 
151
152
153
...
33
34
35
36
37
38
39
40
41
42
43
...
55
56
57
58
59
60
61
...
65
66
67
 
68
69
70
71
...
135
136
137
138
139
140
141
...
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
0
@@ -33,6 +33,11 @@ require 'openssl'       # to generate the HMAC message digest
0
 #   integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
0
 #   such as 'MD5', 'RIPEMD160', 'SHA256', etc.
0
 #
0
+# * <tt>:stable_session_id</tt>: The Cookie Session Store doesn't maintain any server side 
0
+#   state by default.A unique session identifier is spawned per request, which may not be 
0
+#   desireable for some applications.Set :stable_session_id to true to maintain a stable     
0
+#   session identifier within the cookie.  
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
@@ -50,6 +55,7 @@ class CGI::Session::CookieStore
0
 
0
   # Called from CGI::Session only.
0
   def initialize(session, options = {})
0
+    options.reverse_merge!( 'stable_session_id' => false )
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
@@ -59,7 +65,7 @@ class CGI::Session::CookieStore
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
+    @session, @secret, @stable_session_id = session, options['secret'], options['stable_session_id']
0
 
0
     # Message digest defaults to SHA1.
0
     @digest = options['digest'] || 'SHA1'
0
@@ -129,6 +135,7 @@ class CGI::Session::CookieStore
0
   private
0
     # Marshal a session hash into safe cookie data. Include an integrity hash.
0
     def marshal(session)
0
+      session = stable_session_id!( session )
0
       data = ActiveSupport::Base64.encode64(Marshal.dump(session)).chop
0
       "#{data}--#{generate_digest(data)}"
0
     end
0
@@ -144,10 +151,25 @@ class CGI::Session::CookieStore
0
           raise TamperedWithCookie
0
         end
0
 
0
-        Marshal.load(ActiveSupport::Base64.decode64(data))
0
+        returning( stable_session_id!( Marshal.load(ActiveSupport::Base64.decode64(data)) ) ) do |data|
0
+          @session.instance_variable_set(:@session_id, data[:session_id]) if @stable_session_id 
0
+        end
0
       end
0
     end
0
 
0
+    def stable_session_id!( data  )
0
+      return data unless @stable_session_id
0
+      ( data ||= {} ).merge( inject_stable_session_id!( data ) )
0
+    end
0
+
0
+    def inject_stable_session_id!( data )
0
+      if data.respond_to?(:key?) && !data.key?( :session_id )
0
+        { :session_id => CGI::Session.generate_unique_id }
0
+      else
0
+        {}
0
+      end  
0
+    end
0
+
0
     # Read the session data cookie.
0
     def read_cookie
0
       @session.cgi.cookies[@cookie_options['name']].first
...
178
179
180
 
 
 
 
 
 
 
 
 
181
182
183
...
209
210
211
 
212
213
214
215
216
217
218
 
219
220
221
 
222
223
224
...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
...
218
219
220
221
222
223
224
225
226
 
227
228
229
230
 
231
232
233
234
0
@@ -178,6 +178,15 @@ class CookieStoreTest < Test::Unit::TestCase
0
     end
0
   end
0
 
0
+  def test_should_inject_a_radom_session_id_within_the_cookie
0
+    set_cookie! cookie_value(:typical)
0
+    new_session( 'stable_session_id' => true ) do |session|
0
+      assert_not_nil session['user_id']
0
+      assert_not_nil session[:session_id]      
0
+      assert_not_equal session.session_id, cookie_value(:typical)
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
@@ -209,16 +218,17 @@ class CookieStoreTest < Test::Unit::TestCase
0
     end
0
 
0
     def new_session(options = {})
0
+      #options.reverse_merge!( 'stable_session_id' => true )
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
 
0
+        session = CGI::Session.new(cgi, @options)
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
+        
0
         yield session if block_given?
0
         session
0
       end

Comments