# == About camping/session.rb
# TODO: Clean everything up. Lots of just plain wrong stuff in here.
#
# This file contains two modules which supply basic sessioning to your Camping app.
# Again, we're dealing with a pretty little bit of code: approx. 60 lines.
#
# * Camping::Models::Session is a module which adds a single sessions table
# to your database.
# * Camping::Session is a module which you will mix into your application (or into
# specific controllers which require sessions) to supply a @state variable
# you can use in controllers and views.
#
# For a basic tutorial, see the *Getting Started* section of the Camping::Session module.
#require 'camping'
require 'base64'
require 'openssl'
module Camping
# The Camping::Session module is designed to be mixed into your application or into specific
# controllers which require sessions. This module defines a service method which
# intercepts all requests handed to those controllers.
#
# == Getting Started
#
# To get sessions working for your application:
#
# 1. require 'camping/session'
# 2. Mixin the module: module YourApp; include Camping::Session end
# 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.
#
# == A Few Notes
#
# * The session is stored in a cookie. Look in @cookies.identity.
# * Session data is only saved if it has changed.
module Session
DIGEST = OpenSSL::Digest::SHA1.new
# 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 cookie it is created. The @state variable is set and if it changes,
# it is saved back into the cookie.
def service(*a)
@session_blob = @input.camping_blob || @cookies.camping_blob
@session_hash = @input.camping_hash || @cookies.camping_hash
decoded_blob, data = '', {}
begin
if @session_blob && @session_hash && secure_blob_hasher(@session_blob) == @session_hash
decoded_blob = Base64.decode64(@session_blob)
data = Marshal.restore(decoded_blob)
end
app = C.name
@state = (data[app] ||= Camping::H[])
hash_before = decoded_blob.hash
return super(*a)
ensure
data[app] = @state
decoded_blob = Marshal.dump(data)
unless hash_before == decoded_blob.hash
@session_blob = Base64.encode64(decoded_blob).gsub("\n", '').strip
@session_hash = secure_blob_hasher(@session_blob)
raise "The session contains to much data" if @session_blob.length > 4096
@cookies.camping_blob = @session_blob
@cookies.camping_hash = @session_hash
end
end
end
def secure_blob_hasher(data)
OpenSSL::HMAC.hexdigest(DIGEST, state_secret, "#{@env.REMOTE_ADDR}#{data}")
end
def state_secret; [__FILE__, File.mtime(__FILE__)].join(":") end
end
end