Class: OmfCommon::Message::XML::Message
- Inherits:
-
OmfCommon::Message
- Object
- OmfCommon::Message
- OmfCommon::Message::XML::Message
- Includes:
- Comparable
- Defined in:
- omf_common/lib/omf_common/message/xml/message.rb
Overview
Constant Summary
Constant Summary
Constants inherited from OmfCommon::Message
OMF_CORE_READ, OMF_CORE_WRITE, OMF_NAMESPACE
Instance Attribute Summary (collapse)
-
- (Object) content
Returns the value of attribute content.
-
- (Object) xml
Returns the value of attribute xml.
Attributes inherited from OmfCommon::Message
Class Method Summary (collapse)
-
+ (Object) create(operation_type, properties = {}, core_elements = {})
Create a OMF message.
- + (Object) fix_canonicalised_xml(str) private
- + (Object) parse(xml, content_type = "text/xml", &block)
Instance Method Summary (collapse)
- - (Object) <=>(another)
- - (Object) _get_core(key) private
- - (Object) _get_property(key, ns = nil) private
- - (Object) _set_core(key, value) private
- - (Object) _set_property(key, value, ns = nil) private
-
- (Object) add_element(key, value = nil, &block)
Short cut for adding xml node.
-
- (Object) add_property(key, value = nil, add_to = :props)
Construct a property xml node.
- - (Object) build_xml
- - (Object) each_bound_request_property(&block)
-
- (Object) each_property(&block)
Iterate each property key value pair.
- - (Object) each_unbound_request_property(&block)
-
- (Object) element_by_xpath_with_default_namespace(xpath_without_ns)
(also: #read_element)
Short cut for grabbing a group of nodes using xpath, but with default namespace.
- - (Object) escape_key(key) private
- - (Boolean) guard?
- - (Boolean) has_properties?
-
- (Message) initialize(content = {}, issuer = nil)
constructor
private
A new instance of Message.
- - (Object) marshall
-
- (Object) print_app_event
Pretty print for application event message.
- - (Object) properties
-
- (Object) read_content(element_name)
We just want to know the content of an non-repeatable element.
-
- (Object) reconstruct_data(node, data_binding = nil)
Reconstruct xml node into Ruby object.
- - (Object) ruby_type_2_prop_type(ruby_class_type) private
-
- (Object) string_value(value)
private
Get string of a value object, escape if object is string.
- - (Object) to_s
-
- (Boolean) valid?
Validate against relaxng schema.
- - (Object) value_node_set(value, key = nil)
Methods inherited from OmfCommon::Message
#[], #[]=, authenticate?, create_inform_message, #create_inform_reply_message, #default_props_ns, #error?, init, #itype, #props_ns, #resource, #resource_address, #success?
Constructor Details
- (Message) initialize(content = {}, issuer = nil) (private)
Returns a new instance of Message
430 431 432 433 434 435 436 437 438 439 440 441 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 430 def initialize(content = {}, issuer = nil) @content = content @content.mid = SecureRandom.uuid @content.ts = Time.now.utc.to_i if (src = content[:src]) @content.src = OmfCommon.comm.create_topic(src) end @issuer = issuer @content.issuer = @issuer # keep track if we sent local certs on a topic. Should do this the first time @certOnTopic = {} end |
Instance Attribute Details
- (Object) content
Returns the value of attribute content
26 27 28 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 26 def content @content end |
- (Object) xml
Returns the value of attribute xml
25 26 27 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 25 def xml @xml end |
Class Method Details
+ (Object) create(operation_type, properties = {}, core_elements = {})
Create a OMF message
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 30 def create(operation_type, properties = {}, core_elements= {}) # For request messages, properties will be an array if properties.kind_of? Array properties = Hashie::Mash.new.tap do |mash| properties.each { |p| mash[p] = nil } end end properties = Hashie::Mash.new(properties) core_elements = Hashie::Mash.new(core_elements) if operation_type.to_sym == :create core_elements[:rtype] ||= properties[:type] end content = core_elements.merge({ operation: operation_type, type: operation_type, properties: properties }) issuer = self.authenticate? ? (core_elements[:issuer] || core_elements[:src]) : nil new(content, issuer) end |
+ (Object) fix_canonicalised_xml(str) (private)
497 498 499 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 497 def self.fix_canonicalised_xml(str) str.gsub(/\n +/, '').gsub(/ xmlns=\"\"/, '') end |
+ (Object) parse(xml, content_type = "text/xml", &block)
56 57 58 59 60 61 62 63 64 65 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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 56 def parse(xml, content_type = "text/xml", &block) raise ArgumentError, 'Need message handling block' unless block content_type ||= "text/xml" # Since by default parent class pass in nil object raise ArgumentError, "Unknown content type: #{content_type}" unless content_type =~ /xml/ raise ArgumentError, 'Can not parse an empty XML into OMF message' if xml.nil? || xml.empty? xml_node = Nokogiri::XML(xml).root if xml_node.name.to_sym == :env # envelope cert = xml_node.element_children.find { |v| v.element_name == 'cert' }.content sig = xml_node.element_children.find { |v| v.element_name == 'sig' }.content iss = xml_node.element_children.find { |v| v.element_name == 'iss' }.content xml_node = xml_node.element_children.find { |v| v.element_name =~ /create|request|configure|release|inform/ } if self.authenticate? pem = "#{OmfCommon::Auth::Certificate::BEGIN_CERT}#{cert}#{OmfCommon::Auth::Certificate::END_CERT}" cert = OmfCommon::Auth::Certificate.create_from_pem(pem) cert.resource_id = iss if cert.nil? warn "Missing certificate of '#{iss}'" return nil end unless OmfCommon::Auth::CertificateStore.instance.verify(cert) warn "Invalid certificate '#{cert.to_s}', NOT signed by CA certs, or its CA cert NOT loaded into cert store." return nil end OmfCommon::Auth::CertificateStore.instance.register(cert) canonicalised_xml_node = fix_canonicalised_xml(xml_node.canonicalize) unless cert.to_x509.public_key.verify(OpenSSL::Digest::SHA256.new(canonicalised_xml_node), Base64.decode64(sig), canonicalised_xml_node) warn "Verfication failed #{canonicalised_xml_node} #{OpenSSL::Digest::SHA256.new(canonicalised_xml_node)}" return nil end end else if self.authenticate? debug "Message not signed: '#{xml}'" return nil end end parsed_msg = self.create(xml_node.name.to_sym, {}, { issuer: cert }).tap do || .xml = xml_node .send(:_set_core, :mid, .xml.attr('mid')) .xml.elements.each do |el| unless %w(digest props guard).include? el.name .send(:_set_core, el.name, .read_content(el.name)) end if el.name == 'props' .read_element('props').first.element_children.each do |prop_node| e_name = prop_node.element_name if (ns_prefix = prop_node.namespace.prefix) e_name = "#{ns_prefix}__#{e_name}" end .send(:_set_property, e_name, .reconstruct_data(prop_node)) end end if el.name == 'guard' .read_element('guard').first.element_children.each do |guard_node| .guard ||= Hashie::Mash.new .guard[guard_node.element_name] = .reconstruct_data(guard_node) end end end if OmfCommon::Measure.enabled? MPMessage.inject(Time.now.to_f, .content.operation.to_s, .content.mid, .content.cid, .content.to_s.gsub("\n",'')) end end block.call(parsed_msg) parsed_msg end |
Instance Method Details
- (Object) <=>(another)
376 377 378 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 376 def <=>(another) @content <=> another.content end |
- (Object) _get_core(key) (private)
447 448 449 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 447 def _get_core(key) @content[key] end |
- (Object) _get_property(key, ns = nil) (private)
456 457 458 459 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 456 def _get_property(key, ns = nil) # TODO what to do here @content.properties[key] end |
- (Object) _set_core(key, value) (private)
443 444 445 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 443 def _set_core(key, value) @content[key] = value end |
- (Object) _set_property(key, value, ns = nil) (private)
451 452 453 454 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 451 def _set_property(key, value, ns = nil) # TODO what to do here @content.properties[key] = value end |
- (Object) add_element(key, value = nil, &block)
Short cut for adding xml node
309 310 311 312 313 314 315 316 317 318 319 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 309 def add_element(key, value = nil, &block) key = escape_key(key) key_node = Niceogiri::XML::Node.new(key) @xml.add_child(key_node) if block block.call(key_node) else key_node.content = value if value end key_node end |
- (Object) add_property(key, value = nil, add_to = :props)
Construct a property xml node
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 226 def add_property(key, value = nil, add_to = :props) key = escape_key(key) if !props_ns.empty? && add_to == :props && key =~ /^(.+)__(.+)$/ key_node = Niceogiri::XML::Node.new($2, nil, { $1 => props_ns[$1] }) else key_node = Niceogiri::XML::Node.new(key) end unless value.nil? key_node.write_attr('type', ruby_type_2_prop_type(value.class)) c_node = value_node_set(value) if c_node.class == Array c_node.each { |c_n| key_node.add_child(c_n) } else key_node.add_child(c_node) end end read_element(add_to).first.add_child(key_node) key_node end |
- (Object) build_xml
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 188 def build_xml @xml = Niceogiri::XML::Node.new(self.operation.to_s, nil, OMF_NAMESPACE) @xml.write_attr(:mid, mid) props_node = Niceogiri::XML::Node.new(:props) guard_node = Niceogiri::XML::Node.new(:guard) props_ns.each do |k, v| props_node.add_namespace_definition(k, v.to_s) end @xml.add_child(props_node) @xml.add_child(guard_node) if _get_core(:guard) (OMF_CORE_READ - [:mid, :guard, :operation]).each do |attr| attr_value = case attr when :itype self.itype(:frcp) when :src self.src.is_a?(OmfCommon::Comm::Topic) ? self.src.address : self.src else self.send(attr) end next unless attr_value add_element(attr, attr_value) unless (self.operation != :release && attr == :res_id) end self.properties.each { |k, v| add_property(k, v) unless k == 'certificate'} self.guard.each { |k, v| add_property(k, v, :guard) } if _get_core(:guard) @xml end |
- (Object) each_bound_request_property(&block)
410 411 412 413 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 410 def each_bound_request_property(&block) raise "Can only be used for request messages. Got #{type}." if type != :request properties.each { |k, v| block.call(k, v) unless v.nil? } end |
- (Object) each_property(&block)
Iterate each property key value pair
400 401 402 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 400 def each_property(&block) properties.each { |k, v| block.call(k, v) } end |
- (Object) each_unbound_request_property(&block)
405 406 407 408 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 405 def each_unbound_request_property(&block) raise "Can only be used for request messages. Got #{type}." if type != :request properties.each { |k, v| block.call(k, v) if v.nil? } end |
- (Object) element_by_xpath_with_default_namespace(xpath_without_ns) Also known as: read_element
Short cut for grabbing a group of nodes using xpath, but with default namespace
322 323 324 325 326 327 328 329 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 322 def element_by_xpath_with_default_namespace(xpath_without_ns) xpath_without_ns = xpath_without_ns.to_s if !default_props_ns.empty? && xpath_without_ns !~ /props|guard|ts|src|mid|rtype|res_id|cid|itype/ @xml.xpath(xpath_without_ns.gsub(/(^|\/{1,2})(\w+)/, "\\1#{rtype.to_s}:\\2"), default_props_ns) else @xml.xpath(xpath_without_ns.gsub(/(^|\/{1,2})(\w+)/, '\1xmlns:\2'), :xmlns => OMF_NAMESPACE) end end |
- (Object) escape_key(key) (private)
477 478 479 480 481 482 483 484 485 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 477 def escape_key(key) key = key.to_s if key =~ /\W+/ warn "Due to the limitation of XML messages, please only use word character (a-z A-Z 0-9 _) in your property names. Offending characters will be replaced with underscore(_)." key = key.gsub(/\W+/, '_') else key end end |
- (Boolean) guard?
388 389 390 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 388 def guard? @content.guard.empty? end |
- (Boolean) has_properties?
384 385 386 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 384 def has_properties? !@content.properties.empty? end |
- (Object) marshall
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 142 def marshall build_xml if self.class.authenticate? src = @content[:src] issuer = @content[:issuer] src = src.address if src.is_a?(OmfCommon::Comm::Topic) cert = OmfCommon::Auth::CertificateStore.instance.cert_for(issuer) if cert && cert.can_sign? debug "Found cert for '#{src} - #{cert}" signature_node = Niceogiri::XML::Node.new(:sig) canonicalised_xml = self.class.fix_canonicalised_xml(@xml.canonicalize) signature = Base64.encode64(cert.key.sign(OpenSSL::Digest::SHA256.new(canonicalised_xml), canonicalised_xml)).encode('utf-8') signature_node.add_child(signature) @envelope = Niceogiri::XML::Node.new(:env, nil, OMF_NAMESPACE) @envelope.add_child(@xml) @envelope.add_child(signature_node) iss_node = Niceogiri::XML::Node.new(:iss) iss_node.add_child(issuer) @envelope.add_child(iss_node) #unless @certOnTopic[k = [topic, src]] # first time for this src on this topic, so let's send the cert along cert_node = Niceogiri::XML::Node.new(:cert) cert_node.add_child(cert.to_pem_compact) @envelope.add_child(cert_node) #ALWAYS ADD CERT @certOnTopic[k] = Time.now #end ['text/xml', @envelope] else error "Missing cert for #{src}. Auth turned on but could not locate a proper cert." ['text/xml', nil] end else ['text/xml', @xml] end end |
- (Object) print_app_event
Pretty print for application event message
394 395 396 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 394 def print_app_event "APP_EVENT (#{read_property(:app)}, ##{read_property(:seq)}, #{read_property(:event)}): #{read_property(:msg)}" end |
- (Object) properties
380 381 382 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 380 def properties @content.properties end |
- (Object) read_content(element_name)
We just want to know the content of an non-repeatable element
337 338 339 340 341 342 343 344 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 337 def read_content(element_name) element_content = read_element("#{element_name}").first.content rescue nil unless element_content.nil? element_content.empty? ? nil : element_content else nil end end |
- (Object) reconstruct_data(node, data_binding = nil)
Reconstruct xml node into Ruby object
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 350 def reconstruct_data(node, data_binding = nil) node_type = node.attr('type') case node_type when 'array' node.element_children.map do |child| reconstruct_data(child, data_binding) end when /hash/ mash ||= Hashie::Mash.new node.element_children.each do |child| mash[child.attr('key') || child.element_name] ||= reconstruct_data(child, data_binding) end mash when /boolean/ node.content == "true" else if node.content.empty? nil elsif data_binding && node_type == 'string' ERB.new(node.content).result(data_binding) else node.content.ducktype end end end |
- (Object) ruby_type_2_prop_type(ruby_class_type) (private)
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 461 def ruby_type_2_prop_type(ruby_class_type) v_type = ruby_class_type.to_s.downcase case v_type when *%w(trueclass falseclass) 'boolean' when *%w(fixnum bignum) 'integer' when /hash|mash/ 'hash' when /symbol/ 'string' else v_type end end |
- (Object) string_value(value) (private)
Get string of a value object, escape if object is string
488 489 490 491 492 493 494 495 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 488 def string_value(value) if value.kind_of? String value = CGI::escape_html(value) else value = value.to_s end value end |
- (Object) to_s
184 185 186 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 184 def to_s @content end |
- (Boolean) valid?
Validate against relaxng schema
294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 294 def valid? build_xml validation = RelaxNGSchema.instance.validate(@xml.document) if validation.empty? true else logger.error validation.map(&:message).join("\n") logger.debug @xml.to_s false end end |
- (Object) value_node_set(value, key = nil)
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'omf_common/lib/omf_common/message/xml/message.rb', line 248 def value_node_set(value, key = nil) case value when Hash [].tap do |array| value.each_pair do |k, v| unless v.nil? k = escape_key(k) n = Niceogiri::XML::Node.new(k, nil, OMF_NAMESPACE) n.write_attr('type', ruby_type_2_prop_type(v.class)) c_node = value_node_set(v, k) if c_node.class == Array c_node.each { |c_n| n.add_child(c_n) } else n.add_child(c_node) end array << n end end end when Array value.map do |v| n = Niceogiri::XML::Node.new('it', nil, OMF_NAMESPACE) n.write_attr('type', ruby_type_2_prop_type(v.class)) c_node = value_node_set(v, 'it') if c_node.class == Array c_node.each { |c_n| n.add_child(c_n) } else n.add_child(c_node) end n end else if key.nil? string_value(value) else key = escape_key(key) n = Niceogiri::XML::Node.new(key, nil, OMF_NAMESPACE) n.add_child(string_value(value)) end end end |