|
|
|
@ -29,6 +29,44 @@ |
|
|
|
|
|
|
|
|
|
# Be sure to restart your server when you modify this file. |
|
|
|
|
|
|
|
|
|
module OpenProject |
|
|
|
|
## |
|
|
|
|
# `expire_store_after` option specifically for the cache store. |
|
|
|
|
# |
|
|
|
|
# Prepend to ActionDispatch::Session::CacheStore to override `write_session`. |
|
|
|
|
# |
|
|
|
|
# We hook into there to override the `expire_after` option so that we can |
|
|
|
|
# customize it on the storage level. I.e. we want sessions to remain longer |
|
|
|
|
# in storage without affecting the session ID cookie's expiration which is |
|
|
|
|
# set one level higher up in the call chain in the rack session middleware. |
|
|
|
|
module ExpireStoreAfterOption |
|
|
|
|
def write_session(env, sid, session, options) |
|
|
|
|
if update_entry_ttl? session, options |
|
|
|
|
opts = options.to_hash |
|
|
|
|
|
|
|
|
|
return super env, sid, session, opts.merge(expire_after: new_expire_after(session, opts)) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
super |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def new_expire_after(session, options) |
|
|
|
|
options[:expire_store_after].call session, options[:expire_after] |
|
|
|
|
rescue StandardError => e |
|
|
|
|
Rails.logger.error( |
|
|
|
|
"Failed to determine new `after_expire` value. " + |
|
|
|
|
"Falling back to original value. (#{e.message} at #{caller.first})" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
options[:expire_after] |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
def update_entry_ttl?(session, options) |
|
|
|
|
session && options[:expire_store_after] && options[:expire_store_after].respond_to?(:call) |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
config = OpenProject::Configuration |
|
|
|
|
|
|
|
|
|
# Enforce session storage for testing |
|
|
|
@ -46,6 +84,33 @@ session_options = { |
|
|
|
|
path: relative_url_root |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if session_store == :cache_store |
|
|
|
|
# env OPENPROJECT_CACHE__STORE__SESSION__USER__TTL__DAYS |
|
|
|
|
session_ttl = config['cache_store_session_user_ttl_days']&.to_i&.days || 3.days |
|
|
|
|
|
|
|
|
|
# Extend session cache entry TTL so that they can stay logged in when their |
|
|
|
|
# session ID cookie's TTL is 'session' where usually the session entry in the |
|
|
|
|
# cache would expire before the session in the browser by default. |
|
|
|
|
session_options[:expire_store_after] = lambda do |session, expire_after| |
|
|
|
|
if session.include? "user_id" # logged-in user |
|
|
|
|
[session_ttl, expire_after].compact.max |
|
|
|
|
else |
|
|
|
|
expire_after # anonymous user |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
method = ActionDispatch::Session::CacheStore.instance_method(:write_session) |
|
|
|
|
unless method.to_s.include?("write_session(env, sid, session, options)") |
|
|
|
|
raise( |
|
|
|
|
"The signature for `ActionDispatch::Session::CacheStore.write_session` " + |
|
|
|
|
"seems to have changed. Please update the " + |
|
|
|
|
"`ExpireStoreAfterOption` module (and this check) in #{__FILE__}" |
|
|
|
|
) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
ActionDispatch::Session::CacheStore.prepend OpenProject::ExpireStoreAfterOption |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
OpenProject::Application.config.session_store session_store, **session_options |
|
|
|
|
|
|
|
|
|
## |
|
|
|
|