Skip to content

Instantly share code, notes, and snippets.

@bricker
Created July 2, 2012 19:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bricker/3035153 to your computer and use it in GitHub Desktop.
Save bricker/3035153 to your computer and use it in GitHub Desktop.
Ruby serialization with JSON and YAML
session['_auth_user_backend'] = 'django.contrib.auth.backends.ModelBackend'
> flash = ActionDispatch::Flash::FlashHash.new()
> flash[:notice] = "Success!"
> flash
#=> #<ActionDispatch::Flash::FlashHash:0x007fc46f95edc8 @used=#<Set: {}>, @closed=false, @flashes={:notice=>"Success!"}, @now=nil>
> encoded = ActiveSupport::JSON.encode(flash)
#=> "[[\"notice\",\"Success!\"]]"
> ActiveSupport::JSON.decode(encoded)
#=> [["notice", "Success!"]]
> puts encoded = YAML.dump(flash)
#=> --- !ruby/object:ActionDispatch::Flash::FlashHash
#=> used: !ruby/object:Set
#=> hash: {}
#=> closed: false
#=> flashes:
#=> :notice: Success!
#=> now:
> YAML.load(encoded)
#=> #<ActionDispatch::Flash::FlashHash:0x007fc46fe972e0 @used=#<Set: {}>, @closed=false, @flashes={:notice=>"Success!"}, @now=nil>
def load(self):
dd = self._session_key.split("--")
# make sure we got both elements
if len(dd) == 2:
data = re.sub('%3D','=',dd[0])
# now make sure digest is valid
if dd[1] == self.generate_digest(data):
# valid. decode and load data
decoded_data = base64.b64decode(data)
# First load with YAML, if there is a YAML syntax error, then load with JSON
try:
print "trying yaml..."
obj = yaml.load(decoded_data)
except ValueError:
print "trying json..."
obj = simplejson.loads(decoded_data)
except:
print "Couldn't load data. A new session will be created."
obj = False
if obj:
print "got object"
# intercept _session_expiry
if obj.has_key("_session_expiry"):
obj['_session_expiry'] = datetime.datetime.fromtimestamp(int(obj['_session_expiry']))
return obj
else:
# if we get here, it was invalid and we should generate a new session
self.create()
return {}
def _get_session_key(self):
obj = getattr(self, '_session_cache', {})
# intercept _session_expiry
if obj.has_key("_session_expiry") and isinstance(obj['_session_expiry'],datetime.datetime):
obj['_session_expiry'] = obj['_session_expiry'].strftime("%s")
# add session_id if it's not present
if not obj.has_key("session_id"):
obj['session_id'] = rand.bytes(16).encode('hex_codec')
# Dump to YAML, then encode as base64
enc = base64.b64encode(yaml.dump(obj))
return "--".join([re.sub('=','%3D',enc),self.generate_digest(enc)])
class YAMLVerifier < ActiveSupport::MessageVerifier
def verify(signed_message)
raise InvalidSignature if signed_message.blank?
data, digest = signed_message.split("--")
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
# First load with @serializer (YAML), if there is a YAML syntax error, then decode with JSON
begin
@serializer.load(::Base64.decode64(data))
rescue Psych::SyntaxError
Rails.logger.info "Caught YAML syntax error. Decoding with JSON."
ActiveSupport::JSON.decode(Base64.decode64(data.gsub('%3D','=')))
end
else
raise InvalidSignature
end
end
def generate(value)
data = ::Base64.strict_encode64(@serializer.dump(convert(value)))
"#{data}--#{generate_digest(data)}"
end
def convert(value)
# If it isn't present, add in session_expiry to support django
if value.is_a?(Hash)
if !value.has_key?("_session_expiry")
value['_session_expiry'] = (Time.now() + 30*86400).strftime("%s") # expire in 30 days
end
end
return value
end
end
module ActionDispatch
class Cookies
class SignedCookieJar
def initialize(parent_jar, secret)
ensure_secret_secure(secret)
@parent_jar = parent_jar
@verifier = YAMLVerifier.new(secret, serializer: YAML)
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment