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