Class: Tina4::Session
- Inherits:
-
Object
- Object
- Tina4::Session
- Defined in:
- lib/tina4/session.rb
Constant Summary collapse
- DEFAULT_OPTIONS =
{ cookie_name: "tina4_session", secret: nil, max_age: 3600, handler: :file, handler_options: {} }.freeze
Instance Attribute Summary collapse
-
#data ⇒ Object
readonly
Returns the value of attribute data.
-
#id ⇒ Object
readonly
Returns the value of attribute id.
Instance Method Summary collapse
- #[](key) ⇒ Object
- #[]=(key, value) ⇒ Object
-
#all ⇒ Object
Return all session data.
- #clear ⇒ Object
- #cookie_header(cookie_name = nil) ⇒ Object
- #delete(key) ⇒ Object
-
#destroy ⇒ Object
Destroy the current session.
-
#flash(key, value = nil) ⇒ Object
Flash data: set a value that is removed after next read.
-
#gc(max_lifetime = nil) ⇒ Object
Garbage collection: remove expired sessions from the handler.
-
#get(key, default = nil) ⇒ Object
Get a session value with optional default.
-
#get_flash(key, default = nil) ⇒ Object
Get flash data by key (alias for flash(key) without value).
-
#get_session_id ⇒ Object
Returns the current session ID string.
-
#has?(key) ⇒ Boolean
Check if a key exists in the session.
-
#initialize(env, options = {}) ⇒ Session
constructor
A new instance of Session.
-
#read(session_id) ⇒ Object
Reads raw session data for a given session ID from backend storage.
-
#regenerate ⇒ Object
Regenerate the session ID while preserving data — returns the new ID.
-
#save ⇒ Object
Persist the session if dirty.
-
#set(key, value) ⇒ Object
Set a session value.
-
#start(session_id = nil) ⇒ Object
Start or resume a session.
- #to_hash ⇒ Object
-
#write(session_id, data, ttl = nil) ⇒ Object
Writes raw session data for a given session ID to backend storage.
Constructor Details
#initialize(env, options = {}) ⇒ Session
Returns a new instance of Session.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/tina4/session.rb', line 17 def initialize(env, = {}) @options = DEFAULT_OPTIONS.merge() # TINA4_SESSION_NAME — overrides cookie_name unless caller explicitly passed one. env_name = ENV["TINA4_SESSION_NAME"] if !.key?(:cookie_name) && env_name && !env_name.empty? @options[:cookie_name] = env_name end # No guessable built-in secret. The session never signs with this value # (IDs are SecureRandom.hex(32)), so we resolve it from TINA4_SECRET only # — nil when unset. This honours the framework's blank-secret discipline # (Auth.ensure_dev_secret never uses a guessable default); Python/Node # sessions carry no secret field at all. @options[:secret] ||= ENV["TINA4_SECRET"] # Backend-failure policy strict flag (parity with Python's # TINA4_SESSION_STRICT). When truthy, read/write/destroy/gc failures # RE-RAISE instead of logging + degrading. @strict = Tina4::Env.is_truthy(ENV["TINA4_SESSION_STRICT"]) @handler = create_handler @id = extract_session_id(env) || SecureRandom.hex(32) @data = load_session @modified = false end |
Instance Attribute Details
#data ⇒ Object (readonly)
Returns the value of attribute data.
15 16 17 |
# File 'lib/tina4/session.rb', line 15 def data @data end |
#id ⇒ Object (readonly)
Returns the value of attribute id.
15 16 17 |
# File 'lib/tina4/session.rb', line 15 def id @id end |
Instance Method Details
#[](key) ⇒ Object
40 41 42 |
# File 'lib/tina4/session.rb', line 40 def [](key) @data[key.to_s] end |
#[]=(key, value) ⇒ Object
44 45 46 47 |
# File 'lib/tina4/session.rb', line 44 def []=(key, value) @data[key.to_s] = value @modified = true end |
#all ⇒ Object
Return all session data
100 101 102 |
# File 'lib/tina4/session.rb', line 100 def all @data.dup end |
#clear ⇒ Object
54 55 56 57 |
# File 'lib/tina4/session.rb', line 54 def clear @data = {} @modified = true end |
#cookie_header(cookie_name = nil) ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/tina4/session.rb', line 182 def ( = nil) name = || @options[:cookie_name] samesite = ENV["TINA4_SESSION_SAMESITE"] || "Lax" # HttpOnly defaults to true (existing behaviour); flip off only when explicitly false. httponly = !%w[false 0 no off].include?((ENV["TINA4_SESSION_HTTPONLY"] || "true").to_s.strip.downcase) # Secure defaults to false; flip on with truthy env var. secure = %w[true 1 yes on].include?((ENV["TINA4_SESSION_SECURE"] || "false").to_s.strip.downcase) parts = ["#{name}=#{@id}", "Path=/"] parts << "HttpOnly" if httponly parts << "Secure" if secure parts << "SameSite=#{samesite}" parts << "Max-Age=#{@options[:max_age]}" parts.join("; ") end |
#delete(key) ⇒ Object
49 50 51 52 |
# File 'lib/tina4/session.rb', line 49 def delete(key) @data.delete(key.to_s) @modified = true end |
#destroy ⇒ Object
Destroy the current session. Should be called right after login or any privilege change to defend against session fixation (see #regenerate).
78 79 80 81 |
# File 'lib/tina4/session.rb', line 78 def destroy safe_destroy(@id) @data = {} end |
#flash(key, value = nil) ⇒ Object
Flash data: set a value that is removed after next read. Call with value to set, call without value to get (and remove).
106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/tina4/session.rb', line 106 def flash(key, value = nil) flash_key = "_flash_#{key}" if value.nil? val = @data.delete(flash_key.to_s) @modified = true if val val else @data[flash_key.to_s] = value @modified = true value end end |
#gc(max_lifetime = nil) ⇒ Object
Garbage collection: remove expired sessions from the handler. A backend failure is logged and swallowed (never crashes the request).
172 173 174 175 176 177 178 179 180 |
# File 'lib/tina4/session.rb', line 172 def gc(max_lifetime = nil) return unless @handler.respond_to?(:gc) max_lifetime ||= @options[:max_age] @handler.gc(max_lifetime) rescue StandardError => e log_backend_error("gc", e) raise if @strict nil end |
#get(key, default = nil) ⇒ Object
Get a session value with optional default
84 85 86 |
# File 'lib/tina4/session.rb', line 84 def get(key, default = nil) @data[key.to_s] || default end |
#get_flash(key, default = nil) ⇒ Object
Get flash data by key (alias for flash(key) without value)
120 121 122 123 |
# File 'lib/tina4/session.rb', line 120 def get_flash(key, default = nil) result = flash(key) result.nil? ? default : result end |
#get_session_id ⇒ Object
Returns the current session ID string.
154 155 156 |
# File 'lib/tina4/session.rb', line 154 def get_session_id @id end |
#has?(key) ⇒ Boolean
Check if a key exists in the session
95 96 97 |
# File 'lib/tina4/session.rb', line 95 def has?(key) @data.key?(key.to_s) end |
#read(session_id) ⇒ Object
Reads raw session data for a given session ID from backend storage. Returns the data hash, or {} on a backend failure (logged + degraded).
160 161 162 |
# File 'lib/tina4/session.rb', line 160 def read(session_id) safe_read(session_id) end |
#regenerate ⇒ Object
Regenerate the session ID while preserving data — returns the new ID. Call this right after login or any privilege change to defend against session fixation (a pre-auth session ID must not survive into the authenticated session). Destroys the old backend record (best-effort) and persists under the new ID.
130 131 132 133 134 135 136 137 |
# File 'lib/tina4/session.rb', line 130 def regenerate old_id = @id @id = SecureRandom.hex(32) safe_destroy(old_id) @modified = true save @id end |
#save ⇒ Object
Persist the session if dirty. On a backend write failure the error is logged and false is returned — the @modified (dirty) flag is RETAINED so a later save can retry. Returns true on a successful (or no-op) write.
66 67 68 69 70 71 72 73 74 |
# File 'lib/tina4/session.rb', line 66 def save return true unless @modified if safe_write(@id, @data) @modified = false true else false # dirty flag retained for retry end end |
#set(key, value) ⇒ Object
Set a session value
89 90 91 92 |
# File 'lib/tina4/session.rb', line 89 def set(key, value) @data[key.to_s] = value @modified = true end |
#start(session_id = nil) ⇒ Object
Start or resume a session. If session_id is given, load that session; otherwise generate a new ID. Returns the session ID string.
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/tina4/session.rb', line 141 def start(session_id = nil) if session_id @id = session_id @data = load_session else @id = SecureRandom.hex(32) @data = {} end @modified = false @id end |
#to_hash ⇒ Object
59 60 61 |
# File 'lib/tina4/session.rb', line 59 def to_hash @data.dup end |
#write(session_id, data, ttl = nil) ⇒ Object
Writes raw session data for a given session ID to backend storage. Returns true on success, false on a backend failure (logged + degraded).
166 167 168 |
# File 'lib/tina4/session.rb', line 166 def write(session_id, data, ttl = nil) safe_write(session_id, data, ttl) end |