lib/rack/session/abstract/id.rb in rack-1.6.11 vs lib/rack/session/abstract/id.rb in rack-1.6.12

- old
+ new

@@ -7,15 +7,42 @@ begin require 'securerandom' rescue LoadError # We just won't get securerandom end +require "digest/sha2" module Rack module Session + class SessionId + ID_VERSION = 2 + + attr_reader :public_id + + def initialize(public_id) + @public_id = public_id + end + + def private_id + "#{ID_VERSION}::#{hash_sid(public_id)}" + end + + alias :cookie_value :public_id + + def empty?; false; end + def to_s; raise; end + def inspect; public_id.inspect; end + + private + + def hash_sid(sid) + Digest::SHA256.hexdigest(sid) + end + end + module Abstract ENV_SESSION_KEY = 'rack.session'.freeze ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze # SessionHash is responsible to lazily load the session from store. @@ -189,11 +216,11 @@ # Is Rack::Utils::Context compatible. # # Not included by default; you must require 'rack/session/abstract/id' # to use. - class ID + class Persisted DEFAULT_OPTIONS = { :key => 'rack.session', :path => '/', :domain => nil, :expire_after => nil, @@ -340,22 +367,26 @@ session_data = session.to_hash.delete_if { |k,v| v.nil? } if not data = set_session(env, session_id, session_data, options) env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.") elsif options[:defer] and not options[:renew] - env["rack.errors"].puts("Deferring cookie for #{session_id}") if $VERBOSE + env["rack.errors"].puts("Deferring cookie for #{session_id.public_id}") if $VERBOSE else cookie = Hash.new - cookie[:value] = data + cookie[:value] = cookie_value(data) cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after] cookie[:expires] = Time.now + options[:max_age] if options[:max_age] set_cookie(env, headers, cookie.merge!(options)) end [status, headers, body] end + def cookie_value(data) + data + end + # Sets the cookie back to the client with session id. We skip the cookie # setting if the value didn't change (sid is the same) or expires was given. def set_cookie(env, headers, cookie) request = Rack::Request.new(env) @@ -390,9 +421,54 @@ # All thread safety and session destroy procedures should occur here. # Should return a new session id or nil if options[:drop] def destroy_session(env, sid, options) raise '#destroy_session not implemented' + end + end + + class PersistedSecure < Persisted + class SecureSessionHash < SessionHash + def [](key) + if key == "session_id" + load_for_read! + id.public_id + else + super + end + end + end + + def generate_sid(*) + public_id = super + + SessionId.new(public_id) + end + + def extract_session_id(*) + public_id = super + public_id && SessionId.new(public_id) + end + + private + + def session_class + SecureSessionHash + end + + def cookie_value(data) + data.cookie_value + end + end + + class ID < Persisted + def self.inherited(klass) + k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID } + unless k.instance_variable_defined?(:"@_rack_warned") + warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE + k.instance_variable_set(:"@_rack_warned", true) + end + super end end end end end