Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix an issue with namespaces when an element is unmarshalled #32

Merged
merged 3 commits into from
Aug 23, 2023

Conversation

IevaVasiljeva
Copy link

What

Change the logic of namespace retrieval for element unmarshalling. The new logic:

  • iterates over the element and its parents;
  • creates a map of prefixes to namespaces, taking into account namespace overriding (ignore a parent namespace if a namespace with the same prefix has already been registered by a child).

Also correctly apply default namespaces (previously they would have been added like xmlns:=value, now they are added as xmlns=value).

Why

A valid XML lead to the following error: expected element <Assertion> in name space urn:oasis:names:tc:SAML:2.0:assertion but have no name space

XML:

<?xml version="1.0" encoding="UTF-8"?><ns3:Response xmlns:ns3="urn:oasis:names:tc:SAML:2.0:protocol" xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns:ns4="http://www.w3.org/2001/04/xmlenc#" Destination="..." ID="..." InResponseTo="..." IssueInstant="..." Version="2.0">
  <Issuer>...</Issuer>
  <ns3:Status>
    <ns3:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </ns3:Status>
  <Assertion ID="..." IssueInstant="...." Version="2.0">
    <Issuer>...</Issuer>
    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      ...
    </Signature>
     .....
  </Assertion>
</ns3:Response>

Copy link

@alexanderzobnin alexanderzobnin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems, there's a bit simpler solution.

	for space, uri := range namespaces {
		prefix := "xmlns:" + space
		if space == "" {
			prefix = "xmlns"
		}
		doc.Root().CreateAttr(prefix, uri)
	}

I was confused by NamespaceURI() function which says that

If the element is part of the XML default namespace, NamespaceURI returns the empty string.

But it actually returns a default namespace. So we can skip the part inside for _, childEl := range el.FindElements("//*") { and just use "xmlns" key for default namespace.

Source of the NamespaceURI(). Here's a check for Space property and if it's empty that means it's a part of default namespace.

// NamespaceURI returns the XML namespace URI associated with the element. If
// the element is part of the XML default namespace, NamespaceURI returns the
// empty string.
func (e *Element) NamespaceURI() string {
	if e.Space == "" {
		return e.findDefaultNamespaceURI()
	}
	return e.findLocalNamespaceURI(e.Space)
}

@IevaVasiljeva
Copy link
Author

So we can skip the part inside for _, childEl := range el.FindElements("//*") { and just use "xmlns" key for default namespace.

That would solve the issue with default namespace not being correctly added to attributes, but it doesn't solve the problem with the namespaces not being picked up correctly. If we keep the for _, childEl := range el.FindElements("//*") { loop, there are two problems:

  1. we are reading namespaces from all elements not only from the parent elements;
  2. we are not picking up namespaces from the attributes.

Copy link

@alexanderzobnin alexanderzobnin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sounds good! Could you maybe add some comments to the namespace searching section so that will be easier to navigate and read the code in the future?

if ns != "" {
namespaces[childEl.Space] = ns
currentElement := el
for currentElement != nil {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a new test case for testing this functionality (elements from the default namespace)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test for it 👍

@IevaVasiljeva IevaVasiljeva merged commit 9900411 into main Aug 23, 2023
3 of 4 checks passed
@IevaVasiljeva IevaVasiljeva deleted the change-namespace-retrieval-logic branch August 23, 2023 15:18
@IevaVasiljeva IevaVasiljeva restored the change-namespace-retrieval-logic branch August 23, 2023 15:21
@IevaVasiljeva IevaVasiljeva deleted the change-namespace-retrieval-logic branch August 23, 2023 15:21
@robotdan
Copy link

Thanks!!! This is great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants