From 07282ba134aa33f9cf57b4336a356b424e769cec Mon Sep 17 00:00:00 2001 From: Magnus Holm Date: Wed, 21 May 2008 17:25:34 +0200 Subject: [PATCH 1/3] Replacing database-sessions with cookie-sessions --- lib/camping/session.rb | 125 ++++++++++------------------------------- 1 file changed, 31 insertions(+), 94 deletions(-) diff --git a/lib/camping/session.rb b/lib/camping/session.rb index 2c9e656..2c0d012 100644 --- a/lib/camping/session.rb +++ b/lib/camping/session.rb @@ -10,78 +10,9 @@ # you can use in controllers and views. # # For a basic tutorial, see the *Getting Started* section of the Camping::Session module. -require 'camping' -require 'camping/db' - -module Camping::Models -# A database table for storing Camping sessions. Contains a unique 32-character hashid, a -# creation timestamp, and a column of serialized data called ivars. -class Session < Base - serialize :ivars - set_primary_key :hashid - - def []=(k, v) # :nodoc: - self.ivars[k] = v - end - def [](k) # :nodoc: - self.ivars[k] rescue nil - end - - protected - RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z'] - def before_create - rand_max = RAND_CHARS.size - sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] } - write_attribute('hashid', sid) - end - - # Generates a new session ID and creates a row for the new session in the database. - def self.generate cookies - sess = Session.create :ivars => Camping::H[] - cookies.camping_sid = sess.hashid - sess - end - - # Gets the existing session based on the camping_sid available in cookies. - # If none is found, generates a new session. - def self.persist cookies - session = nil - if cookies.camping_sid - session = Camping::Models::Session.find_by_hashid cookies.camping_sid - end - unless session - session = Camping::Models::Session.generate cookies - end - session - end - - # Builds the session table in the database. To be used in your application's - # create method. - # - # Like so: - # - # def Blog.create - # Camping::Models::Session.create_schema - # unless Blog::Models::Post.table_exists? - # ActiveRecord::Schema.define(&Blog::Models.schema) - # end - # end - # - def self.create_schema - unless table_exists? - ActiveRecord::Schema.define do - create_table :sessions, :force => true, :id => false do |t| - t.column :hashid, :string, :limit => 32, :null => false - t.column :created_at, :datetime - t.column :ivars, :text - end - add_index :sessions, [:hashid], :unique => true - end - reset_column_information - end - end -end -end +#require 'camping' +require 'base64' +require 'digest/sha2' module Camping # The Camping::Session module is designed to be mixed into your application or into specific @@ -94,38 +25,44 @@ module Camping # # 1. require 'camping/session' # 2. Mixin the module: module YourApp; include Camping::Session end -# 3. In your application's create method, add a call to Camping::Models::Session.create_schema +# 3. Define a secret (and keep it secret): module YourApp; @@state_secret = "SECRET!"; end # 4. Throughout your application, use the @state var like a hash to store your application's data. -# -# If you are unfamiliar with the create method, see -# http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod. # # == A Few Notes # -# * The session ID is stored in a cookie. Look in @cookies.camping_sid. -# * The session data is stored in the sessions table in your database. -# * All mounted Camping apps using this class will use the same database table. -# * However, your application's data is stored in its own hash. +# * The session is stored in a cookie. Look in @cookies.identity. # * Session data is only saved if it has changed. module Session # This service method, when mixed into controllers, intercepts requests # and wraps them with code to start and close the session. If a session isn't found - # in the database it is created. The @state variable is set and if it changes, - # it is saved back into the database. + # in the cookie it is created. The @state variable is set and if it changes, + # it is saved back into the cookie. def service(*a) - session = Camping::Models::Session.persist @cookies - app = self.class.name.gsub(/^(\w+)::.+$/, '\1') - @state = (session[app] ||= Camping::H[]) - hash_before = Marshal.dump(@state).hash - return super(*a) + if @cookies.identity + blob, secure_hash = @cookies.identity.to_s.split(':', 2) + blob = Base64.decode64(blob) + data = Marshal.restore(blob) + data = {} unless secure_blob_hasher(blob).strip.downcase == secure_hash.strip.downcase + else + blob = '' + data = {} + end + + app = self.class.name.gsub(/^(\w+)::.+$/, '\1') + @state = (data[app] ||= Camping::H[]) + hash_before = blob.hash + return super(*a) ensure - if session - hash_after = Marshal.dump(@state).hash - unless hash_before == hash_after - session[app] = @state - session.save - end - end + data[app] = @state + blob = Marshal.dump(data) + unless hash_before == blob.hash + secure_hash = secure_blob_hasher(blob) + @response.set_cookie("identity", Base64.encode64(blob).gsub("\n", '').strip + ':' + secure_hash) + end + end + + def secure_blob_hasher(data) + Digest::SHA256.hexdigest(self.class.module_eval('@@state_secret') + data) end end end From 1fcc124d0b930aed029396005cd1fe082d71269e Mon Sep 17 00:00:00 2001 From: Magnus Holm Date: Thu, 22 May 2008 12:05:33 +0200 Subject: [PATCH 2/3] Raise error when the cookie exceeds 4kB --- lib/camping/session.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/camping/session.rb b/lib/camping/session.rb index 2c0d012..7f44ead 100644 --- a/lib/camping/session.rb +++ b/lib/camping/session.rb @@ -57,7 +57,9 @@ def service(*a) blob = Marshal.dump(data) unless hash_before == blob.hash secure_hash = secure_blob_hasher(blob) - @response.set_cookie("identity", Base64.encode64(blob).gsub("\n", '').strip + ':' + secure_hash) + content = Base64.encode64(blob).gsub("\n", '').strip + ':' + secure_hash + raise "The session contains to much data" if content.length > 4096 + @response.set_cookie("identity", content) end end From 0013f681968862ea239ad77020f2bede6fa0a74c Mon Sep 17 00:00:00 2001 From: Magnus Holm Date: Thu, 22 May 2008 13:21:05 +0200 Subject: [PATCH 3/3] Cleaning up a bit --- lib/camping/session.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/camping/session.rb b/lib/camping/session.rb index 7f44ead..731102b 100644 --- a/lib/camping/session.rb +++ b/lib/camping/session.rb @@ -42,7 +42,7 @@ def service(*a) blob, secure_hash = @cookies.identity.to_s.split(':', 2) blob = Base64.decode64(blob) data = Marshal.restore(blob) - data = {} unless secure_blob_hasher(blob).strip.downcase == secure_hash.strip.downcase + data = {} unless secure_blob_hasher(blob).strip == secure_hash.strip else blob = '' data = {}