Class: Cybertrain::Session

Inherits:
Object
  • Object
show all
Defined in:
cybertrain/session.rb

Overview

A cookie-store session: a flat Hash<String,String> that round-trips through a signed, base64-encoded cookie value. Design D8 restricts values to String – there is no marshalling format to smuggle other types through, so nothing else needs to be ruled out at the type level.

Controller#session in an action. Keys and values are Strings (a Symbol key is its name; a value is stored as value.to_s, so session[:user_id] = 5 reads back as "5").

The cookie is signed with secret_key_base (HMAC-SHA256), not encrypted: the client can read it, but a changed or forged cookie is dropped and the request starts with an empty session. It is sent only when the session changed, as _cybertrain_session with Path=/; HttpOnly; SameSite=Lax, Secure in production and a two-week Max-Age (the name, the Max-Age and Secure are Config settings). "_csrf_token" and "_flash" are the framework’s keys.

Examples:

session[:user_id] = user.id
User.find(session[:user_id]) if session.key?(:user_id)
session.delete(:user_id)

Instance Method Summary collapse

Instance Method Details

#[](key) ⇒ String?

Parameters:

  • key (String, Symbol)

Returns:

  • (String, nil)


43
44
45
# File 'cybertrain/session.rb', line 43

def [](key)
  @data[key.to_s]
end

#[]=(key, value) ⇒ String

Parameters:

  • key (String, Symbol)
  • value (Object) —

    stored as value.to_s

Returns:

  • (String)


51
52
53
54
55
56
# File 'cybertrain/session.rb', line 51

def []=(key, value)
  k = key.to_s
  v = value.to_s
  @changed = true unless @data[k] == v
  @data[k] = v
end

#clear ⇒ Object

Empties the session, the CSRF token included, so forms rendered before it no longer submit (403). Use it on sign-out.



83
84
85
86
# File 'cybertrain/session.rb', line 83

def clear
  @changed = true unless @data.empty?
  @data.clear
end

#csrf_token! ⇒ String

Returns this session’s CSRF token, minting and storing one the first time it is asked for. Forms built with form_with and button_to already carry it; send it yourself as the X-CSRF-Token header or an authenticity_token parameter from JavaScript or a hand-written form.

This is a typed instance method (rather than a module method taking a Session argument) so Spinel resolves self statically at every call site: a free function taking session as a plain parameter widens it to an untyped poly value once it is called through a real SessionStore -> Context chain, and then session[TOKEN_KEY] = token silently does nothing (tests: test/csrf_chain.rb, test/csrf_conditional.rb).

Returns:

  • (String)


114
115
116
117
118
119
120
121
# File 'cybertrain/session.rb', line 114

def csrf_token!
  token = @data[CSRF_TOKEN_KEY]
  if token.nil? || token.empty?
    token = Crypto.random_token
    self[CSRF_TOKEN_KEY] = token
  end
  token
end

#delete(key) ⇒ String?

Returns the removed value.

Parameters:

  • key (String, Symbol)

Returns:

  • (String, nil) —

    the removed value



61
62
63
64
65
# File 'cybertrain/session.rb', line 61

def delete(key)
  k = key.to_s
  @changed = true if @data.key?(k)
  @data.delete(k)
end

#key?(key) ⇒ Boolean

Parameters:

  • key (String, Symbol)

Returns:

  • (Boolean)


70
71
72
# File 'cybertrain/session.rb', line 70

def key?(key)
  @data.key?(key.to_s)
end

#keys ⇒ Array<String>

Returns:

  • (Array<String>)


76
77
78
# File 'cybertrain/session.rb', line 76

def keys
  @data.keys
end

#to_h ⇒ Hash{String => String}

Returns a copy.

Returns:

  • (Hash{String => String}) —

    a copy



90
91
92
# File 'cybertrain/session.rb', line 90

def to_h
  @data.dup
end