123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::Encryption::MessageSerializer

Relationships & Source Files
Inherits: Object
Defined in: activerecord/lib/active_record/encryption/message_serializer.rb

Overview

A message serializer that serializes Messages with JSON.

The generated structure is pretty simple:

{
  p: <payload>,
  h: {
    header1: value1,
    header2: value2,
    #...
  }
}

Both the payload and the header values are encoded with Base64 to prevent JSON parsing errors and encoding issues when storing the resulting serialized data.

Instance Attribute Summary

Instance Method Summary

Instance Attribute Details

#binary? ⇒ Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 39

def binary?
  false
end

Instance Method Details

#decode_if_needed(value) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 88

def decode_if_needed(value)
  if value.is_a?(String)
    ::Base64.strict_decode64(value)
  else
    value
  end
rescue ArgumentError, TypeError
  raise Errors::Encoding
end

#dump(message)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 34

def dump(message)
  raise ActiveRecord::Encryption::Errors::ForbiddenClass unless message.is_a?(ActiveRecord::Encryption::Message)
  JSON.dump message_to_json(message)
end

#encode_if_needed(value) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 80

def encode_if_needed(value)
  if value.is_a?(String)
    ::Base64.strict_encode64 value
  else
    value
  end
end

#headers_to_json(headers) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 74

def headers_to_json(headers)
  headers.transform_values do |value|
    value.is_a?(ActiveRecord::Encryption::Message) ? message_to_json(value) : encode_if_needed(value)
  end
end

#load(serialized_content)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 27

def load(serialized_content)
  data = JSON.parse(serialized_content)
  parse_message(data, 1)
rescue JSON::ParserError
  raise ActiveRecord::Encryption::Errors::Encoding
end

#message_to_json(message) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 67

def message_to_json(message)
  {
    p: encode_if_needed(message.payload),
    h: headers_to_json(message.headers)
  }
end

#parse_message(data, level) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 44

def parse_message(data, level)
  validate_message_data_format(data, level)
  ActiveRecord::Encryption::Message.new(payload: decode_if_needed(data["p"]), headers: parse_properties(data["h"], level))
end

#parse_properties(headers, level) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 59

def parse_properties(headers, level)
  ActiveRecord::Encryption::Properties.new.tap do |properties|
    headers&.each do |key, value|
      properties[key] = value.is_a?(Hash) ? parse_message(value, level + 1) : decode_if_needed(value)
    end
  end
end

#validate_message_data_format(data, level) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/encryption/message_serializer.rb', line 49

def validate_message_data_format(data, level)
  if level > 2
    raise ActiveRecord::Encryption::Errors::Decryption, "More than one level of hash nesting in headers is not supported"
  end

  unless data.is_a?(Hash) && data.has_key?("p")
    raise ActiveRecord::Encryption::Errors::Decryption, "Invalid data format: hash without payload"
  end
end