mirror of
https://github.com/TracksApp/tracks.git
synced 2026-01-04 00:08:50 +01:00
unfreeze rails
This commit is contained in:
parent
514045e8cb
commit
ee751a5ced
1563 changed files with 0 additions and 216754 deletions
|
|
@ -1,181 +0,0 @@
|
|||
require 'rack/utils'
|
||||
|
||||
module ActionController
|
||||
module Session
|
||||
class AbstractStore
|
||||
ENV_SESSION_KEY = 'rack.session'.freeze
|
||||
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
||||
|
||||
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
||||
SET_COOKIE = 'Set-Cookie'.freeze
|
||||
|
||||
class SessionHash < Hash
|
||||
def initialize(by, env)
|
||||
super()
|
||||
@by = by
|
||||
@env = env
|
||||
@loaded = false
|
||||
end
|
||||
|
||||
def session_id
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"ActionController::Session::AbstractStore::SessionHash#session_id " +
|
||||
"has been deprecated. Please use request.session_options[:id] instead.", caller)
|
||||
@env[ENV_SESSION_OPTIONS_KEY][:id]
|
||||
end
|
||||
|
||||
def [](key)
|
||||
load! unless @loaded
|
||||
super
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
load! unless @loaded
|
||||
super
|
||||
end
|
||||
|
||||
def to_hash
|
||||
h = {}.replace(self)
|
||||
h.delete_if { |k,v| v.nil? }
|
||||
h
|
||||
end
|
||||
|
||||
def data
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"ActionController::Session::AbstractStore::SessionHash#data " +
|
||||
"has been deprecated. Please use #to_hash instead.", caller)
|
||||
to_hash
|
||||
end
|
||||
|
||||
def inspect
|
||||
load! unless @loaded
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
def loaded?
|
||||
@loaded
|
||||
end
|
||||
|
||||
def load!
|
||||
stale_session_check! do
|
||||
id, session = @by.send(:load_session, @env)
|
||||
(@env[ENV_SESSION_OPTIONS_KEY] ||= {})[:id] = id
|
||||
replace(session)
|
||||
@loaded = true
|
||||
end
|
||||
end
|
||||
|
||||
def stale_session_check!
|
||||
yield
|
||||
rescue ArgumentError => argument_error
|
||||
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
|
||||
begin
|
||||
# Note that the regexp does not allow $1 to end with a ':'
|
||||
$1.constantize
|
||||
rescue LoadError, NameError => const_error
|
||||
raise ActionController::SessionRestoreError, "Session contains objects whose class definition isn\\'t available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: \#{const_error.message} [\#{const_error.class}])\n"
|
||||
end
|
||||
|
||||
retry
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
:key => '_session_id',
|
||||
:path => '/',
|
||||
:domain => nil,
|
||||
:expire_after => nil,
|
||||
:secure => false,
|
||||
:httponly => true,
|
||||
:cookie_only => true
|
||||
}
|
||||
|
||||
def initialize(app, options = {})
|
||||
# Process legacy CGI options
|
||||
options = options.symbolize_keys
|
||||
if options.has_key?(:session_path)
|
||||
options[:path] = options.delete(:session_path)
|
||||
end
|
||||
if options.has_key?(:session_key)
|
||||
options[:key] = options.delete(:session_key)
|
||||
end
|
||||
if options.has_key?(:session_http_only)
|
||||
options[:httponly] = options.delete(:session_http_only)
|
||||
end
|
||||
|
||||
@app = app
|
||||
@default_options = DEFAULT_OPTIONS.merge(options)
|
||||
@key = @default_options[:key]
|
||||
@cookie_only = @default_options[:cookie_only]
|
||||
end
|
||||
|
||||
def call(env)
|
||||
session = SessionHash.new(self, env)
|
||||
|
||||
env[ENV_SESSION_KEY] = session
|
||||
env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
|
||||
|
||||
response = @app.call(env)
|
||||
|
||||
session_data = env[ENV_SESSION_KEY]
|
||||
options = env[ENV_SESSION_OPTIONS_KEY]
|
||||
|
||||
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
|
||||
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
|
||||
|
||||
sid = options[:id] || generate_sid
|
||||
|
||||
unless set_session(env, sid, session_data.to_hash)
|
||||
return response
|
||||
end
|
||||
|
||||
cookie = Rack::Utils.escape(@key) + '=' + Rack::Utils.escape(sid)
|
||||
cookie << "; domain=#{options[:domain]}" if options[:domain]
|
||||
cookie << "; path=#{options[:path]}" if options[:path]
|
||||
if options[:expire_after]
|
||||
expiry = Time.now + options[:expire_after]
|
||||
cookie << "; expires=#{expiry.httpdate}"
|
||||
end
|
||||
cookie << "; Secure" if options[:secure]
|
||||
cookie << "; HttpOnly" if options[:httponly]
|
||||
|
||||
headers = response[1]
|
||||
unless headers[SET_COOKIE].blank?
|
||||
headers[SET_COOKIE] << "\n#{cookie}"
|
||||
else
|
||||
headers[SET_COOKIE] = cookie
|
||||
end
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
private
|
||||
def generate_sid
|
||||
ActiveSupport::SecureRandom.hex(16)
|
||||
end
|
||||
|
||||
def load_session(env)
|
||||
request = Rack::Request.new(env)
|
||||
sid = request.cookies[@key]
|
||||
unless @cookie_only
|
||||
sid ||= request.params[@key]
|
||||
end
|
||||
sid, session = get_session(env, sid)
|
||||
[sid, session]
|
||||
end
|
||||
|
||||
def get_session(env, sid)
|
||||
raise '#get_session needs to be implemented.'
|
||||
end
|
||||
|
||||
def set_session(env, sid, session_data)
|
||||
raise '#set_session needs to be implemented.'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
module ActionController
|
||||
module Session
|
||||
# This cookie-based session store is the Rails default. Sessions typically
|
||||
# contain at most a user_id and flash message; both fit within the 4K cookie
|
||||
# size limit. Cookie-based sessions are dramatically faster than the
|
||||
# alternatives.
|
||||
#
|
||||
# If you have more than 4K of session data or don't want your data to be
|
||||
# visible to the user, pick another session store.
|
||||
#
|
||||
# CookieOverflow is raised if you attempt to store more than 4K of data.
|
||||
#
|
||||
# A message digest is included with the cookie to ensure data integrity:
|
||||
# a user cannot alter his +user_id+ without knowing the secret key
|
||||
# included in the hash. New apps are generated with a pregenerated secret
|
||||
# in config/environment.rb. Set your own for old apps you're upgrading.
|
||||
#
|
||||
# Session options:
|
||||
#
|
||||
# * <tt>:secret</tt>: An application-wide key string or block returning a
|
||||
# string called per generated digest. The block is called with the
|
||||
# CGI::Session instance as an argument. It's important that the secret
|
||||
# is not vulnerable to a dictionary attack. Therefore, you should choose
|
||||
# a secret consisting of random numbers and letters and more than 30
|
||||
# characters. Examples:
|
||||
#
|
||||
# :secret => '449fe2e7daee471bffae2fd8dc02313d'
|
||||
# :secret => Proc.new { User.current_user.secret_key }
|
||||
#
|
||||
# * <tt>:digest</tt>: The message digest algorithm used to verify session
|
||||
# integrity defaults to 'SHA1' but may be any digest provided by OpenSSL,
|
||||
# such as 'MD5', 'RIPEMD160', 'SHA256', etc.
|
||||
#
|
||||
# To generate a secret key for an existing application, run
|
||||
# "rake secret" and set the key in config/environment.rb.
|
||||
#
|
||||
# Note that changing digest or secret invalidates all existing sessions!
|
||||
class CookieStore
|
||||
# Cookies can typically store 4096 bytes.
|
||||
MAX = 4096
|
||||
SECRET_MIN_LENGTH = 30 # characters
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
:key => '_session_id',
|
||||
:domain => nil,
|
||||
:path => "/",
|
||||
:expire_after => nil,
|
||||
:httponly => true
|
||||
}.freeze
|
||||
|
||||
ENV_SESSION_KEY = "rack.session".freeze
|
||||
ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
|
||||
HTTP_SET_COOKIE = "Set-Cookie".freeze
|
||||
|
||||
# Raised when storing more than 4K of session data.
|
||||
class CookieOverflow < StandardError; end
|
||||
|
||||
def initialize(app, options = {})
|
||||
# Process legacy CGI options
|
||||
options = options.symbolize_keys
|
||||
if options.has_key?(:session_path)
|
||||
options[:path] = options.delete(:session_path)
|
||||
end
|
||||
if options.has_key?(:session_key)
|
||||
options[:key] = options.delete(:session_key)
|
||||
end
|
||||
if options.has_key?(:session_http_only)
|
||||
options[:httponly] = options.delete(:session_http_only)
|
||||
end
|
||||
|
||||
@app = app
|
||||
|
||||
# The session_key option is required.
|
||||
ensure_session_key(options[:key])
|
||||
@key = options.delete(:key).freeze
|
||||
|
||||
# The secret option is required.
|
||||
ensure_secret_secure(options[:secret])
|
||||
@secret = options.delete(:secret).freeze
|
||||
|
||||
@digest = options.delete(:digest) || 'SHA1'
|
||||
@verifier = verifier_for(@secret, @digest)
|
||||
|
||||
@default_options = DEFAULT_OPTIONS.merge(options).freeze
|
||||
|
||||
freeze
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
|
||||
env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
|
||||
|
||||
status, headers, body = @app.call(env)
|
||||
|
||||
session_data = env[ENV_SESSION_KEY]
|
||||
options = env[ENV_SESSION_OPTIONS_KEY]
|
||||
|
||||
if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after]
|
||||
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?)
|
||||
session_data = marshal(session_data.to_hash)
|
||||
|
||||
raise CookieOverflow if session_data.size > MAX
|
||||
|
||||
cookie = Hash.new
|
||||
cookie[:value] = session_data
|
||||
unless options[:expire_after].nil?
|
||||
cookie[:expires] = Time.now + options[:expire_after]
|
||||
end
|
||||
|
||||
cookie = build_cookie(@key, cookie.merge(options))
|
||||
unless headers[HTTP_SET_COOKIE].blank?
|
||||
headers[HTTP_SET_COOKIE] << "\n#{cookie}"
|
||||
else
|
||||
headers[HTTP_SET_COOKIE] = cookie
|
||||
end
|
||||
end
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
|
||||
private
|
||||
# Should be in Rack::Utils soon
|
||||
def build_cookie(key, value)
|
||||
case value
|
||||
when Hash
|
||||
domain = "; domain=" + value[:domain] if value[:domain]
|
||||
path = "; path=" + value[:path] if value[:path]
|
||||
# According to RFC 2109, we need dashes here.
|
||||
# N.B.: cgi.rb uses spaces...
|
||||
expires = "; expires=" + value[:expires].clone.gmtime.
|
||||
strftime("%a, %d-%b-%Y %H:%M:%S GMT") if value[:expires]
|
||||
secure = "; secure" if value[:secure]
|
||||
httponly = "; HttpOnly" if value[:httponly]
|
||||
value = value[:value]
|
||||
end
|
||||
value = [value] unless Array === value
|
||||
cookie = Rack::Utils.escape(key) + "=" +
|
||||
value.map { |v| Rack::Utils.escape(v) }.join("&") +
|
||||
"#{domain}#{path}#{expires}#{secure}#{httponly}"
|
||||
end
|
||||
|
||||
def load_session(env)
|
||||
request = Rack::Request.new(env)
|
||||
session_data = request.cookies[@key]
|
||||
data = unmarshal(session_data) || persistent_session_id!({})
|
||||
[data[:session_id], data]
|
||||
end
|
||||
|
||||
# Marshal a session hash into safe cookie data. Include an integrity hash.
|
||||
def marshal(session)
|
||||
@verifier.generate(persistent_session_id!(session))
|
||||
end
|
||||
|
||||
# Unmarshal cookie data to a hash and verify its integrity.
|
||||
def unmarshal(cookie)
|
||||
persistent_session_id!(@verifier.verify(cookie)) if cookie
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
nil
|
||||
end
|
||||
|
||||
def ensure_session_key(key)
|
||||
if key.blank?
|
||||
raise ArgumentError, 'A key is required to write a ' +
|
||||
'cookie containing the session data. Use ' +
|
||||
'config.action_controller.session = { :key => ' +
|
||||
'"_myapp_session", :secret => "some secret phrase" } in ' +
|
||||
'config/environment.rb'
|
||||
end
|
||||
end
|
||||
|
||||
# To prevent users from using something insecure like "Password" we make sure that the
|
||||
# secret they've provided is at least 30 characters in length.
|
||||
def ensure_secret_secure(secret)
|
||||
# There's no way we can do this check if they've provided a proc for the
|
||||
# secret.
|
||||
return true if secret.is_a?(Proc)
|
||||
|
||||
if secret.blank?
|
||||
raise ArgumentError, "A secret is required to generate an " +
|
||||
"integrity hash for cookie session data. Use " +
|
||||
"config.action_controller.session = { :key => " +
|
||||
"\"_myapp_session\", :secret => \"some secret phrase of at " +
|
||||
"least #{SECRET_MIN_LENGTH} characters\" } " +
|
||||
"in config/environment.rb"
|
||||
end
|
||||
|
||||
if secret.length < SECRET_MIN_LENGTH
|
||||
raise ArgumentError, "Secret should be something secure, " +
|
||||
"like \"#{ActiveSupport::SecureRandom.hex(16)}\". The value you " +
|
||||
"provided, \"#{secret}\", is shorter than the minimum length " +
|
||||
"of #{SECRET_MIN_LENGTH} characters"
|
||||
end
|
||||
end
|
||||
|
||||
def verifier_for(secret, digest)
|
||||
key = secret.respond_to?(:call) ? secret.call : secret
|
||||
ActiveSupport::MessageVerifier.new(key, digest)
|
||||
end
|
||||
|
||||
def generate_sid
|
||||
ActiveSupport::SecureRandom.hex(16)
|
||||
end
|
||||
|
||||
def persistent_session_id!(data)
|
||||
(data ||= {}).merge!(inject_persistent_session_id(data))
|
||||
end
|
||||
|
||||
def inject_persistent_session_id(data)
|
||||
requires_session_id?(data) ? { :session_id => generate_sid } : {}
|
||||
end
|
||||
|
||||
def requires_session_id?(data)
|
||||
if data
|
||||
data.respond_to?(:key?) && !data.key?(:session_id)
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
begin
|
||||
require_library_or_gem 'memcache'
|
||||
|
||||
module ActionController
|
||||
module Session
|
||||
class MemCacheStore < AbstractStore
|
||||
def initialize(app, options = {})
|
||||
# Support old :expires option
|
||||
options[:expire_after] ||= options[:expires]
|
||||
|
||||
super
|
||||
|
||||
@default_options = {
|
||||
:namespace => 'rack:session',
|
||||
:memcache_server => 'localhost:11211'
|
||||
}.merge(@default_options)
|
||||
|
||||
@pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options)
|
||||
unless @pool.servers.any? { |s| s.alive? }
|
||||
raise "#{self} unable to find server during initialization."
|
||||
end
|
||||
@mutex = Mutex.new
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
def get_session(env, sid)
|
||||
sid ||= generate_sid
|
||||
begin
|
||||
session = @pool.get(sid) || {}
|
||||
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
||||
session = {}
|
||||
end
|
||||
[sid, session]
|
||||
end
|
||||
|
||||
def set_session(env, sid, session_data)
|
||||
options = env['rack.session.options']
|
||||
expiry = options[:expire_after] || 0
|
||||
@pool.set(sid, session_data, expiry)
|
||||
return true
|
||||
rescue MemCache::MemCacheError, Errno::ECONNREFUSED
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
# MemCache wasn't available so neither can the store be
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue