lib/global_session/session/abstract.rb in global_session-3.2.10 vs lib/global_session/session/abstract.rb in global_session-3.3.0
- old
+ new
@@ -12,11 +12,11 @@
#
# ===Raise
# InvalidSession:: if the session contained in the cookie has been invalidated
# ExpiredSession:: if the session contained in the cookie has expired
# MalformedCookie:: if the cookie was corrupt or malformed
- # SecurityError:: if signature is invalid or cookie is not signed by a trusted authority
+ # InvalidSignature:: if signature is invalid or cookie is not signed by a trusted authority
def initialize(directory, cookie=nil)
@directory = directory
@signed = {}
@insecure = {}
@@ -73,10 +73,84 @@
# @return [true,false] true if this session was created in-process, false if it was initialized from a cookie
def new_record?
@cookie.nil?
end
+ # Return the keys that are currently present in the global session.
+ #
+ # === Return
+ # keys(Array):: List of keys contained in the global session
+ def keys
+ @signed.keys + @insecure.keys
+ end
+
+ # Return the values that are currently present in the global session.
+ #
+ # === Return
+ # values(Array):: List of values contained in the global session
+ def values
+ @signed.values + @insecure.values
+ end
+
+ # 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
+ def each_pair(&block) # :yields: |key, value|
+ @signed.each_pair(&block)
+ @insecure.each_pair(&block)
+ end
+
+ # 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
+ def [](key)
+ key = key.to_s #take care of symbol-style keys
+ @signed[key] || @insecure[key]
+ end
+
+ # 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 JSON
+ 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
+
# Determine whether the session is valid. This method simply delegates to the
# directory associated with this session.
#
# === Return
# valid(true|false):: True if the session is valid, false otherwise
@@ -86,11 +160,11 @@
# Determine whether any state has changed since the session was loaded.
#
# @return [Boolean] true if something has changed
def dirty?
- !!(new_record? || @dirty_timestamps)
+ !!(new_record? || @dirty_timestamps || @dirty_secure || @dirty_insecure)
end
# Determine whether the global session schema allows a given key to be placed
# in the global session.
#
@@ -114,10 +188,38 @@
@signed.has_key?(key) || @insecure.has_key?(key)
end
alias key? has_key?
+ # Delete a key from the global session attributes. If the key exists,
+ # mark the global session dirty
+ #
+ # @param [String] the key to delete
+ # @return [Object] the value of the key deleted, or nil if not found
+ def delete(key)
+ key = key.to_s #take care of symbol-style keys
+ raise GlobalSession::InvalidSession unless valid?
+
+ if @schema_signed.include?(key)
+ authority_check
+
+ # Only mark dirty if the key actually exists
+ @dirty_secure = true if @signed.keys.include? key
+ value = @signed.delete(key)
+ elsif @schema_insecure.include?(key)
+
+ # Only mark dirty if the key actually exists
+ @dirty_insecure = true if @insecure.keys.include? key
+ value = @insecure.delete(key)
+ else
+ raise ArgumentError, "Attribute '#{key}' is not specified in global session configuration"
+ end
+
+ return value
+ end
+
+
# Invalidate this session by reporting its UUID to the Directory.
#
# === Return
# unknown(Object):: Returns whatever the Directory returns
def invalidate!
@@ -138,10 +240,14 @@
@dirty_timestamps = true
end
private
+ def generate_id
+ RightSupport::Data::Base64URL.encode(SecureRandom.random_bytes(8))
+ end
+
def authority_check # :nodoc:
unless @directory.local_authority_name
raise GlobalSession::NoAuthority, 'Cannot change secure session attributes; we are not an authority'
end
end
@@ -153,9 +259,22 @@
def create_from_scratch
raise NotImplementedError, "Subclass responsibility"
end
def create_invalid
- raise NotImplementedError, "Subclass responsibility"
+ @id = nil
+ @created_at = Time.now.utc
+ @expired_at = created_at
+ @signed = {}
+ @insecure = {}
+ @authority = nil
end
+
+ # This is called by Object#clone and is used to augment our "shallow clone"
+ # behavior so that we don't share state hashes between clones.
+ def initialize_copy(source)
+ super
+ @signed = ::RightSupport::Data::HashTools.deep_clone2(@signed)
+ @insecure = ::RightSupport::Data::HashTools.deep_clone2(@insecure)
+ end
end
-end
\ No newline at end of file
+end