class SimpleSession::Base

Attributes

request[R]
session[R]

Public Class Methods

new(app, options = {}) click to toggle source
# File lib/simple_session/base.rb, line 11
def initialize app, options = {}
  @app          = app
  @key          = options.fetch :key, 'rack.session'
  @secret       = options.fetch :secret, get_secret_errors(options)
  @options_key  = options.fetch :options_key, 'rack.session.options'
  @default_opts = DEFAULT_OPTS.merge!(options)
end

Public Instance Methods

add_session(headers) click to toggle source
# File lib/simple_session/base.rb, line 87
def add_session headers
  cookie = Hash.new
  cookie.merge!(session.fetch(:options, @default_opts))
  cookie[:value] = encrypt session

  set_cookie_header headers, @key, cookie
end
call(env) click to toggle source
# File lib/simple_session/base.rb, line 43
def call env
  # Decrypt request session and store it
  extract_session env

  # Load session into app env
  load_environment env

  # Pass on request
  status, headers, body = @app.call env

  # Check session for changes and update
  update_options if options_changed?
  update_session if session_changed?

  # Encrypt and add session to headers
  add_session headers

  [status, headers, body]
end
cipher_key() click to toggle source
# File lib/simple_session/base.rb, line 181
def cipher_key
  hmac("A red, red fox has had three socks but all have holes" + @secret)
end
decrypt(data) click to toggle source
# File lib/simple_session/base.rb, line 133
def decrypt data
  # Decode Base64
  b = data.unpack('m0').first

  # Parse Signature
  h1   = b[0, 32]
  data = b[32..-1]
  h2   = data.reverse[0, 32].reverse
  data = data.reverse[32..-1].reverse

  if h1 == signature.to_s && h2 == signature.to_s
    # Decipher
    Marshal.load unload_cipher data
  else
    raise SecurityError
  end
end
digest() click to toggle source
# File lib/simple_session/base.rb, line 151
def digest
  OpenSSL::Digest.new 'sha256', @secret
end
encrypt(data) click to toggle source
# File lib/simple_session/base.rb, line 119
def encrypt data
  # Serialize
  m = Marshal.dump data

  # Cipher
  c = load_cipher m

  # Sign
  h = signature + c + signature

  # Encode Base64
  [h].pack('m0')
end
extract_session(env) click to toggle source
# File lib/simple_session/base.rb, line 63
def extract_session env
  begin
    @request = Rack::Request.new env
    @session = req_session ? decrypt(req_session) : new_session_hash
    session.merge!(options_hash)
    raise ArgumentError, "Unable to decrypt session" unless session

  rescue Exception => e
    @session = new_session_hash.merge!(options_hash)
    print e.message
  end

end
get_secret_errors(options) click to toggle source
# File lib/simple_session/base.rb, line 19
def get_secret_errors options
  secret = options[:secret]
  missing_msg = %[
    SimpleSession requires a secret like this:
    use SimpleSession::Session, secret: 'some secret'
  ]

  short_msg  = %[
    SimpleSession require a secret with a minimum length of 32
    use SimpleSession::Session, secret: SecureRandom.hex(32)
  ]

  raise ArgumentError, missing_msg unless secret
  raise ArgumentError, short_msg   unless secret.length >= 32
end
hmac(data) click to toggle source
# File lib/simple_session/base.rb, line 155
def hmac data
  OpenSSL::HMAC.digest digest, @secret, data
end
load_cipher(marshaled_data) click to toggle source
# File lib/simple_session/base.rb, line 163
def load_cipher marshaled_data
  cipher = new_cipher
  cipher.encrypt
  iv = cipher.random_iv
  cipher.iv  = iv
  cipher.key = cipher_key

  iv + cipher.update(marshaled_data) + cipher.final
end
load_environment(env) click to toggle source
# File lib/simple_session/base.rb, line 82
def load_environment env
  env[@key] = session.dup
  env[@options_key] = session[:options].dup
end
new_cipher() click to toggle source
# File lib/simple_session/base.rb, line 159
def new_cipher
  OpenSSL::Cipher::AES.new(256, :CBC)
end
new_session_hash() click to toggle source
# File lib/simple_session/base.rb, line 39
def new_session_hash
  { session_id: SecureRandom.hex(32) }
end
options_changed?() click to toggle source
# File lib/simple_session/base.rb, line 103
def options_changed? 
  request.session_options != session[:options]
end
options_hash() click to toggle source
# File lib/simple_session/base.rb, line 77
def options_hash
  o = session[:options] || OptionHash.new(@default_opts).opts
  { options: o }
end
req_session() click to toggle source
# File lib/simple_session/base.rb, line 35
def req_session
  request.cookies[@key] if request
end
session_changed?() click to toggle source
# File lib/simple_session/base.rb, line 107
def session_changed?
  request.session != session
end
signature() click to toggle source
# File lib/simple_session/base.rb, line 115
def signature
  hmac(cipher_key)
end
unload_cipher(data) click to toggle source
# File lib/simple_session/base.rb, line 173
def unload_cipher data
  cipher = new_cipher
  cipher.decrypt
  cipher.iv  = data[0, 16]
  cipher.key = cipher_key
  cipher.update(data[16..-1]) + cipher.final
end
update_options() click to toggle source
# File lib/simple_session/base.rb, line 99
def update_options
  session[:options] = {options: OptionHash.new(request.session_options).opts}
end
update_session() click to toggle source
# File lib/simple_session/base.rb, line 111
def update_session
  @session = request.session
end