lib/roda/plugins/sessions.rb in roda-3.10.0 vs lib/roda/plugins/sessions.rb in roda-3.11.0
- old
+ new
@@ -19,12 +19,11 @@
module RodaPlugins
# The sessions plugin adds support for sessions using cookies. It is the recommended
# way to support sessions in Roda applications.
#
# The session cookies are encrypted with AES-256-CTR and then signed with HMAC-SHA-256.
- # By default, session data over a certain size is compressed to reduced space, and
- # is padded to reduce information leaked based on the session size.
+ # By default, session data is padded to reduce information leaked based on the session size.
#
# Sessions are serialized via JSON, so session information should only store data that
# allows roundtrips via JSON (String, Integer, Float, Array, Hash, true, false, and nil).
# In particular, note that Symbol does not round trip via JSON, so symbols should not be
# used in sessions when this plugin is used. This plugin sets the
@@ -32,30 +31,24 @@
# for better integration with plugins that can use either symbol or string session or
# flash keys. Unlike Rack::Session::Cookie, the session is stored as a plain ruby hash,
# and does not convert all keys to strings.
#
# All sessions are timestamped and session expiration is enabled by default, with sessions
- # being valid for 30 days maximum and 7 days since last use. Session creation time is
+ # being valid for 30 days maximum and 7 days since last use by default. Session creation time is
# reset whenever the session is empty when serialized and also whenever +clear_session+
# is called while processing the request.
#
- # Session secrets can be rotated, and if so both the cipher and HMAC secrets should be
- # rotated at the same time. See options below.
+ # Session secrets can be rotated. See options below.
#
# The sessions plugin can transparently upgrade sessions from Rack::Session::Cookie
# if the default Rack::Session::Cookie coder and HMAC are used, see options below.
# It is recommended to only enable transparent upgrades for a brief transition period,
# and remove support for them once old sessions have converted or timed out.
#
- # While session data will be compressed by default for sessions over a certain size,
- # if the final cookie is too large (>=4096 bytes), a Roda::RodaPlugins::Sessions::CookieTooLarge
+ # If the final cookie is too large (>=4096 bytes), a Roda::RodaPlugins::Sessions::CookieTooLarge
# exception will be raised.
#
- # If the flash plugin is used, the sessions plugin should be loaded after the flash
- # plugin, so that the flash plugin rotates the flash in the session before the sessions
- # plugin serializes the session.
- #
# = Required Options
#
# The session cookies this plugin uses are both encrypted and signed, so two separate
# secrets are used internally. However, for ease of use, these secrets are combined into
# a single +:secret+ option. The +:secret+ option must be a string of at least 64 bytes
@@ -68,11 +61,13 @@
# <tt>httponly: true, path: '/', same_site: :lax</tt> so that the cookie is not accessible
# to javascript, allowed for all paths, and will not be used for cross-site non-GET requests
# that. If the +:secure+ option is not present in the hash, then
# <tt>secure: true</tt> is also set if the request is made over HTTPS. If this option is
# given, it will be merged into the default cookie options.
- # :gzip_over :: For session data over this many bytes, compress it with the deflate algorithm (default: 128).
+ # :gzip_over :: For session data over this many bytes, compress it with the deflate algorithm (default: nil,
+ # so never compress). Note that compression should not be enabled if you are storing data in
+ # the session derived from user input and also storing sensitive data in the session.
# :key :: The cookie name to use (default: <tt>'roda.session'</tt>)
# :max_seconds :: The maximum number of seconds to allow for total session lifetime, starting with when
# the session was originally created. Default is <tt>86400*30</tt> (30 days). Can be set to
# +nil+ to disable session lifetime checks.
# :max_idle_sessions :: The maximum number of seconds to allow since the session was last updated.
@@ -138,11 +133,11 @@
# padding :: >=0 padding bytes specified in bitmap, filled with random data, can be ignored.
# serialized data :: >=2 bytes of serialized data in JSON format. If the bitmap indicates
# deflate compression, this contains the deflate compressed data.
module Sessions
DEFAULT_COOKIE_OPTIONS = {:httponly=>true, :path=>'/'.freeze, :same_site=>:lax}.freeze
- DEFAULT_OPTIONS = {:key => 'roda.session'.freeze, :max_seconds=>86400*30, :max_idle_seconds=>86400*7, :pad_size=>32, :gzip_over=>128, :skip_within=>3600}.freeze
+ DEFAULT_OPTIONS = {:key => 'roda.session'.freeze, :max_seconds=>86400*30, :max_idle_seconds=>86400*7, :pad_size=>32, :gzip_over=>nil, :skip_within=>3600}.freeze
DEFLATE_BIT = 0x1000
PADDING_MASK = 0x0fff
SESSION_CREATED_AT = 'roda.session.created_at'.freeze
SESSION_UPDATED_AT = 'roda.session.updated_at'.freeze
SESSION_SERIALIZED = 'roda.session.serialized'.freeze
@@ -151,10 +146,14 @@
# Exception class used when creating a session cookie that would exceed the
# allowable cookie size limit.
class CookieTooLarge < RodaError
end
+ def self.load_dependencies(app, opts=OPTS)
+ app.plugin :_after_hook
+ end
+
# Split given secret into a cipher secret and an hmac secret.
def self.split_secret(name, secret)
raise RodaError, "sessions plugin :#{name} option must be a String" unless secret.is_a?(String)
raise RodaError, "invalid sessions plugin :#{name} option length: #{secret.bytesize}, must be >=64" unless secret.bytesize >= 64
hmac_secret = secret = secret.dup.force_encoding('BINARY')
@@ -192,32 +191,30 @@
app.opts[:sessions] = opts.freeze
app.opts[:sessions_convert_symbols] = true unless app.opts.has_key?(:sessions_convert_symbols)
end
module InstanceMethods
- # If session information has been set in the request environment,
- # update the rack response headers to set the session cookie in
- # the response.
- def call
- res = super
-
- if session = env['rack.session']
- @_request.persist_session(res[1], session)
- end
-
- res
- end
-
# Clear data from the session, and update the request environment
# so that the session cookie will use a new creation timestamp
# instead of the previous creation timestamp.
def clear_session
session.clear
env.delete(SESSION_CREATED_AT)
env.delete(SESSION_UPDATED_AT)
nil
end
+
+ private
+
+ # If session information has been set in the request environment,
+ # update the rack response headers to set the session cookie in
+ # the response.
+ def _roda_after_50(res)
+ if res && (session = env['rack.session'])
+ @_request.persist_session(res[1], session)
+ end
+ end
end
module RequestMethods
# Load the session information from the cookie. With the sessions
# plugin, you must call this method to get the session, instead of
@@ -399,11 +396,12 @@
return
end
bitmap = 0
json_length = json_data.bytesize
+ gzip_over = opts[:gzip_over]
- if json_length > opts[:gzip_over]
+ if gzip_over && json_length > gzip_over
json_data = Zlib.deflate(json_data)
json_length = json_data.bytesize
bitmap |= DEFLATE_BIT
end