Class: GlobalSession::Session::V2

Inherits:
Abstract
  • Object
show all
Defined in:
lib/global_session/session/v2.rb

Overview

Global session V2 uses msgpack serialization and no compression. Its msgpack structure is an Array with the following format:

[<uuid_string>,
 <signing_authority_string>,
 <creation_timestamp_integer>,
 <expiration_timestamp_integer>,
 {<signed_data_hash>},
 {<unsigned_data_hash>},
 <binary_signature_string>]

The design goal of V2 is to minimize the size of the base64-encoded session state in order to make GlobalSession more amenable to use as a browser cookie.

Limitations of V2 include the following:

  • Some Ruby implementations (e.g. JRuby) lack a msgpack library

  • The sign and verify algorithms, while safe, do not comply fully with PKCS7; they rely on the OpenSSL low-level crypto API instead of using the higher-level EVP (envelope) API.

Constant Summary collapse

HEADER =

Pattern that matches strings that are probably a V2 session cookie.

/^l9/

Instance Attribute Summary

Attributes inherited from Abstract

#authority, #created_at, #directory, #expired_at, #id, #insecure, #signed

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Abstract

#delete, #has_key?, #initialize, #inspect, #invalidate!, #new_record?, #renew!, #supports_key?, #to_h, #valid?

Constructor Details

This class inherits a constructor from GlobalSession::Session::Abstract

Class Method Details

Utility method to decode a cookie; good for console debugging. This performs no validation or security check of any sort.

Parameters

cookie(String)

well-formed global session cookie



55
56
57
58
# File 'lib/global_session/session/v2.rb', line 55

def self.decode_cookie(cookie)
  msgpack = GlobalSession::Encoding::Base64Cookie.load(cookie)
  return GlobalSession::Encoding::Msgpack.load(msgpack)
end

Instance Method Details

#[](key) ⇒ Object

Lookup a value by its key.

Parameters

key(String)

the key

Return

value(Object)

The value associated with key, or nil if key is not present



141
142
143
144
# File 'lib/global_session/session/v2.rb', line 141

def [](key)
  key = key.to_s #take care of symbol-style keys
  @signed[key] || @insecure[key]
end

#[]=(key, value) ⇒ Object

Set a value in the global session hash. If the supplied key is denoted as secure by the global session schema, causes a new signature to be computed when the session is next serialized.

Parameters

key(String)

The key to set

value(Object)

The value to set

Return

value(Object)

Always returns the value that was set

Raise

InvalidSession

if the session has been invalidated (and therefore can’t be written to)

ArgumentError

if the configuration doesn’t define the specified key as part of the global session

NoAuthority

if the specified key is secure and the local node is not an authority

UnserializableType

if the specified value can’t be serialized as msgpack



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/global_session/session/v2.rb', line 162

def []=(key, value)
  key = key.to_s #take care of symbol-style keys
  raise GlobalSession::InvalidSession unless valid?

  if @schema_signed.include?(key)
    authority_check
    @signed[key] = value
    @dirty_secure = true
  elsif @schema_insecure.include?(key)
    @insecure[key] = value
    @dirty_insecure = true
  else
    raise ArgumentError, "Attribute '#{key}' is not specified in global session configuration"
  end

  return value
end

#dirty?Boolean

Determine whether any state has changed since the session was loaded.

Returns:

  • (Boolean)

    true if something has changed



102
103
104
# File 'lib/global_session/session/v2.rb', line 102

def dirty?
  !!(super || @dirty_secure || @dirty_insecure)
end

#each_pair(&block) ⇒ Object

Iterate over each key/value pair

Block

An iterator which will be called with each key/value pair

Return

Returns the value of the last expression evaluated by the block



129
130
131
132
# File 'lib/global_session/session/v2.rb', line 129

def each_pair(&block) # :yields: |key, value|
  @signed.each_pair(&block)
  @insecure.each_pair(&block)
end

#keysObject

Return the keys that are currently present in the global session.

Return

keys(Array)

List of keys contained in the global session



110
111
112
# File 'lib/global_session/session/v2.rb', line 110

def keys
  @signed.keys + @insecure.keys
end

#signature_digestObject

Return the SHA1 hash of the most recently-computed RSA signature of this session. This isn’t really intended for the end user; it exists so the Web framework integration code can optimize request speed by caching the most recently verified signature in the local session and avoid re-verifying it on every request.

Return

digest(String)

SHA1 hex-digest of most-recently-computed signature



187
188
189
# File 'lib/global_session/session/v2.rb', line 187

def signature_digest
  @signature ? digest(@signature) : nil
end

#to_sObject

Serialize the session to a form suitable for use with HTTP cookies. If any secure attributes have changed since the session was instantiated, compute a fresh RSA signature.

Return

cookie(String)

Base64Cookie-encoded, Msgpack-serialized global session



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/global_session/session/v2.rb', line 66

def to_s
  if @cookie && !dirty?
    #use cached cookie if nothing has changed
    return @cookie
  end

  hash = {'id' => @id,
          'tc' => @created_at.to_i, 'te' => @expired_at.to_i,
          'ds' => @signed}

  if @signature && !(@dirty_timestamps || @dirty_secure)
    #use cached signature unless we've changed secure state
    authority = @authority
  else
    authority_check
    authority = @directory.local_authority_name
    hash['a'] = authority
    signed_hash = RightSupport::Crypto::SignedHash.new(
        hash.reject { |k,v| ['dx', 's'].include?(k) },
        @directory.private_key,
        encoding: GlobalSession::Encoding::Msgpack)
    @signature = signed_hash.sign(@expired_at)
  end

  hash['dx'] = @insecure
  hash['s'] = @signature
  hash['a'] = authority

  array = attribute_hash_to_array(hash)
  msgpack = GlobalSession::Encoding::Msgpack.dump(array)
  return GlobalSession::Encoding::Base64Cookie.dump(msgpack)
end

#valuesObject

Return the values that are currently present in the global session.

Return

values(Array)

List of values contained in the global session



118
119
120
# File 'lib/global_session/session/v2.rb', line 118

def values
  @signed.values + @insecure.values
end