Skip to content

Commit

Permalink
Merge pull request #8 from mkristian/timeout
Browse files Browse the repository at this point in the history
added server side time_to_live of session data
  • Loading branch information
namelessjon committed Jun 12, 2014
2 parents a0c1ea1 + d06598f commit 2138c04
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 15 deletions.
50 changes: 36 additions & 14 deletions lib/encrypted_cookie.rb
Expand Up @@ -20,21 +20,30 @@ module Session
# :domain => 'foo.com',
# :path => '/',
# :expire_after => 2592000
# :time_to_live => 600
#
# All parameters are optional except :secret.
#
# The default for the session time-to-live is 30 minutes. You can set
# the timeout on per session base by adding the expiration time in the
# session:
# session[Rack::Session::EncryptedCookie::EXPIRES] = Time.now + 120
#
# Note that you shouldn't trust the expire_after parameter in the cookie
# for session expiry as that can be altered by the recipient. Instead,
# store a timestamp in the session
# for session expiry as that can be altered by the recipient. Instead,
# use time_to_live which is server side check.
class EncryptedCookie

EXPIRES = '_encrypted_cookie_expires_'

def initialize(app, options={})
@app = app
@key = options[:key] || "rack.session"
@secret = options[:secret]
fail "Error! A secret is required to use encrypted cookies. Do something like this:\n\nuse Rack::Session::EncryptedCookie, :secret => YOUR_VERY_LONG_VERY_RANDOM_SECRET_KEY_HERE" unless @secret
@default_options = {:domain => nil,
:path => "/",
:time_to_live => 1800,
:expire_after => nil}.merge(options)
@encryptor = Encryptor.new(@secret)
end
Expand All @@ -47,32 +56,45 @@ def call(env)

private

def remove_expiration(session_data)
expires = session_data.delete(EXPIRES)
if expires and expires < Time.now
session_data.clear
end
end

def load_session(env)
request = Rack::Request.new(env)
env["rack.session.options"] = @default_options.dup

session_data = request.cookies[@key]
session_data = @encryptor.decrypt(session_data)
session_data = Marshal.load(session_data)
remove_expiration(session_data)

if session_data
session_data = @encryptor.decrypt(session_data)
end
env["rack.session"] = session_data
rescue
env["rack.session"] = Hash.new
end

begin
session_data = Marshal.load(session_data)
env["rack.session"] = session_data
rescue
env["rack.session"] = Hash.new
def add_expiration(session_data, options)
if options[:time_to_live] && !session_data.key?(EXPIRES)
expires = Time.now + options[:time_to_live]
session_data.merge!({EXPIRES => expires})
end

env["rack.session.options"] = @default_options.dup
end

def commit_session(env, status, headers, body)
session_data = Marshal.dump(env["rack.session"])
options = env["rack.session.options"]

session_data = env["rack.session"]
add_expiration(session_data, options)
session_data = Marshal.dump(session_data)
session_data = @encryptor.encrypt(session_data)

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?
Expand Down
25 changes: 24 additions & 1 deletion spec/encrypted_cookie_spec.rb
Expand Up @@ -23,10 +23,14 @@ def unpack_cookie

class EncryptedApp < Sinatra::Application
disable :show_exceptions
use Rack::Session::EncryptedCookie, :secret => 'foo' * 10
use Rack::Session::EncryptedCookie, :secret => 'foo' * 10, :time_to_live => 0.1
get '/' do
"session: " + session.inspect
end
get '/timeout/:value' do
session[Rack::Session::EncryptedCookie::EXPIRES] = Time.now + params[:value].to_i
"timeout set"
end
get '/set/:key/:value' do
session[params[:key]] = params[:value]
"all set"
Expand Down Expand Up @@ -99,6 +103,25 @@ def app
get '/'
last_response.body.should == 'session: {}'
end
it "should reset the session on timeout" do
get '/set/foo/bar'
last_response.body.should == 'all set'
get '/'
last_response.body.should == 'session: {"foo"=>"bar"}'
sleep 0.08
get '/'
last_response.body.should == 'session: {"foo"=>"bar"}'
sleep 0.08
get '/'
last_response.body.should == 'session: {"foo"=>"bar"}'
sleep 0.1
get '/'
last_response.body.should == 'session: {}'
get '/timeout/0'
last_response.body.should == 'timeout set'
get '/'
last_response.body.should == 'session: {}'
end
end

describe UnencryptedApp do
Expand Down

0 comments on commit 2138c04

Please sign in to comment.