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