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
|
require 'openssl'
require 'rack/request'
require 'rack/response'
module Rack
module Session
# Rack::Session::Cookie provides simple cookie based session management.
# The session is a Ruby Hash stored as base64 encoded marshalled data
# set to :key (default: rack.session).
# When the secret key is set, cookie data is checked for data integrity.
#
# Example:
#
# use Rack::Session::Cookie, :key => 'rack.session',
# :domain => 'foo.com',
# :path => '/',
# :expire_after => 2592000,
# :secret => 'change_me'
#
# All parameters are optional.
class Cookie
def initialize(app, options={})
@app = app
@key = options[:key] || "rack.session"
@secret = options[:secret]
@default_options = {:domain => nil,
:path => "/",
:expire_after => nil}.merge(options)
end
def call(env)
load_session(env)
status, headers, body = @app.call(env)
commit_session(env, status, headers, body)
end
private
def load_session(env)
request = Rack::Request.new(env)
session_data = request.cookies[@key]
if @secret && session_data
session_data, digest = session_data.split("--")
session_data = nil unless Rack::Utils.secure_compare(digest, generate_hmac(session_data))
end
begin
session_data = session_data.unpack("m*").first
session_data = Marshal.load(session_data)
env["rack.session"] = session_data
rescue
env["rack.session"] = Hash.new
end
env["rack.session.options"] = @default_options.dup
end
def commit_session(env, status, headers, body)
session_data = Marshal.dump(env["rack.session"])
session_data = [session_data].pack("m*")
if @secret
session_data = "#{session_data}--#{generate_hmac(session_data)}"
end
if session_data.size > (4096 - @key.size)
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K. Content dropped.")
else
options = env["rack.session.options"]
cookie = Hash.new
cookie[:value] = session_data
cookie[:expires] = Time.now + options[:expire_after] unless options[:expire_after].nil?
Utils.set_cookie_header!(headers, @key, cookie.merge(options))
end
[status, headers, body]
end
def generate_hmac(data)
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, data)
end
end
end
end
|