diff --git a/lib/xmldsig/reference.rb b/lib/xmldsig/reference.rb index 739137c..1ea84f0 100644 --- a/lib/xmldsig/reference.rb +++ b/lib/xmldsig/reference.rb @@ -2,6 +2,9 @@ module Xmldsig class Reference attr_accessor :reference, :errors, :id_attr + XPOINTER_ROOT = '#xpointer(/)' + XPOINTER_REG_ID = /#xpointer\(id\('(.*)'\)\)/ + class ReferencedNodeNotFound < Exception; end @@ -32,18 +35,21 @@ def referenced_node "Could not find referenced document with ContentId #{content_id}" ) end - else - id = reference_uri[1..-1] - referenced_node_xpath = @id_attr ? "//*[@#{@id_attr}=$uri]" : "//*[@ID=$uri or @wsu:Id=$uri]" - variable_bindings = { 'uri' => id } - if ref = document.dup.at_xpath(referenced_node_xpath, NAMESPACES, variable_bindings) - ref + elsif reference_uri.start_with?("#xpointer") + return document.dup.root if reference_uri == XPOINTER_ROOT + + matched = reference_uri.match(XPOINTER_REG_ID) + if !matched.nil? + get_node_by_id!(@id_attr, matched[1]) else raise( - ReferencedNodeNotFound, - "Could not find the referenced node #{id}'" + ReferencedNodeNotFound, + "Could not find referenced document with referenceUri #{reference_uri}" ) end + else + id = reference_uri[1..-1] + get_node_by_id!(@id_attr, id) end else document.dup.root @@ -96,5 +102,20 @@ def validate_digest_value @errors << :digest_value end end + + private + + def get_node_by_id!(id_attr, id) + referenced_node_xpath = id_attr ? "//*[@#{id_attr}=$uri]" : "//*[@ID=$uri or @wsu:Id=$uri]" + variable_bindings = { 'uri' => id } + if ref = document.dup.at_xpath(referenced_node_xpath, NAMESPACES, variable_bindings) + ref + else + raise( + ReferencedNodeNotFound, + "Could not find the referenced node #{id}'" + ) + end + end end end diff --git a/spec/lib/xmldsig/reference_spec.rb b/spec/lib/xmldsig/reference_spec.rb index 54994ec..3d57aa3 100644 --- a/spec/lib/xmldsig/reference_spec.rb +++ b/spec/lib/xmldsig/reference_spec.rb @@ -32,6 +32,20 @@ ) end + it "returns the referenced node by xpointer root" do + allow(reference).to receive(:reference_uri).and_return("#xpointer(/)") + expect(reference.referenced_node.to_s).to eq( + document.root.to_s + ) + end + + it "returns the referenced node by xpointer id" do + allow(reference).to receive(:reference_uri).and_return("#xpointer(id('foo'))") + expect(reference.referenced_node.to_s).to eq( + document.at_xpath("//*[@ID='foo']").to_s + ) + end + it "returns the referenced node by parent" do allow(reference).to receive(:reference_uri).and_return("") expect(reference.referenced_node.to_s).to eq(