keycloak-uncached

Merge pull request #4499 from pskopek/anyTypeAttribute [KEYCLOAK-4374]

9/28/2017 5:39:27 AM

Details

diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java
index a58cdd3..8b39ff0 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java
@@ -49,10 +49,13 @@ import org.w3c.dom.Element;
 import javax.xml.datatype.XMLGregorianCalendar;
 import javax.xml.namespace.QName;
 import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
+import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.events.Attribute;
 import javax.xml.stream.events.EndElement;
 import javax.xml.stream.events.StartElement;
 import javax.xml.stream.events.XMLEvent;
+import java.io.StringWriter;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
@@ -294,7 +297,22 @@ public class SAMLParserUtil {
         StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
         StaxParserUtil.validate(startElement, JBossSAMLConstants.ATTRIBUTE_VALUE.get());
 
-        Attribute type = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XSI_NSURI.get(), "type", "xsi"));
+        Attribute type = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XSI_NSURI.get(), JBossSAMLConstants.TYPE.get(), JBossSAMLURIConstants.XSI_PREFIX.get()));
+        Attribute nil = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XSI_NSURI.get(), "nil", JBossSAMLURIConstants.XSI_PREFIX.get()));
+        if (nil != null) {
+            String nilValue = StaxParserUtil.getAttributeValue(nil);
+            if (nilValue != null
+                    && (nilValue.equalsIgnoreCase("true") || nilValue.equals("1"))) {
+                String elementText = StaxParserUtil.getElementText(xmlEventReader);
+                if  (elementText == null || elementText.isEmpty()) {
+                    return null;
+                } else {
+                    throw logger.nullValueError("nil attribute is not in SAML20 format");
+                }
+            } else {
+                throw logger.parserRequiredAttribute(JBossSAMLURIConstants.XSI_PREFIX.get() + ":nil");
+            }
+        }
         if (type == null) {
             if (StaxParserUtil.hasTextAhead(xmlEventReader)) {
                 return StaxParserUtil.getElementText(xmlEventReader);
@@ -316,25 +334,54 @@ public class SAMLParserUtil {
                 return "";
             }
 
-            throw logger.unsupportedType(StaxParserUtil.getStartElementName(startElement));
+            // when no type attribute assigned -> assume anyType
+            return parseAnyTypeAsString(xmlEventReader);
         }
         //      RK Added an additional type check for base64Binary type as calheers is passing this type
         String typeValue = StaxParserUtil.getAttributeValue(type);
         if (typeValue.contains(":string")) {
             return StaxParserUtil.getElementText(xmlEventReader);
         } else if (typeValue.contains(":anyType")) {
-            // TODO: for now assume that it is a text value that can be parsed and set as the attribute value
-            return StaxParserUtil.getElementText(xmlEventReader);
+            return parseAnyTypeAsString(xmlEventReader);
         } else if(typeValue.contains(":base64Binary")){
             return StaxParserUtil.getElementText(xmlEventReader);
         } else if(typeValue.contains(":boolean")){
             return StaxParserUtil.getElementText(xmlEventReader);
         }
 
-
         throw logger.parserUnknownXSI(typeValue);
     }
 
+    public static String parseAnyTypeAsString(XMLEventReader xmlEventReader) throws ParsingException {
+        try {
+            XMLEvent event = xmlEventReader.peek();
+            if (event.isStartElement()) {
+                event = xmlEventReader.nextTag();
+                StringWriter sw = new StringWriter();
+                XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(sw);
+                //QName tagName = event.asStartElement().getName();
+                int tagLevel = 1;
+                do {
+                    writer.add(event);
+                    event = (XMLEvent) xmlEventReader.next();
+                    if (event.isStartElement()) {
+                        tagLevel++;
+                    }
+                    if (event.isEndElement()) {
+                        tagLevel--;
+                    }
+                } while (xmlEventReader.hasNext() && tagLevel > 0);
+                writer.add(event);
+                writer.flush();
+                return sw.toString();
+            } else {
+                return StaxParserUtil.getElementText(xmlEventReader);
+            }
+        } catch (Exception e) {
+            throw logger.parserError(e);
+        }
+    }
+
     /**
      * Parse the AuthnStatement inside the assertion
      *
diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
index 2f2157c..68baf0d 100644
--- a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
+++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java
@@ -16,25 +16,34 @@
  */
 package org.keycloak.saml.processing.core.parsers.saml;
 
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.PrivateKey;
+
+import org.junit.Before;
+import org.junit.Test;
 import org.keycloak.common.util.Base64;
 import org.keycloak.common.util.DerUtils;
 import org.keycloak.dom.saml.v2.assertion.AssertionType;
+import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
+import org.keycloak.dom.saml.v2.assertion.AttributeType;
 import org.keycloak.dom.saml.v2.assertion.NameIDType;
 import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
-import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
-
-import java.io.InputStream;
-import java.security.PrivateKey;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-import static org.hamcrest.CoreMatchers.*;
-
 import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
 import org.keycloak.dom.saml.v2.protocol.ResponseType;
-
-import org.junit.Before;
+import org.keycloak.saml.common.exceptions.ParsingException;
+import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
 import org.w3c.dom.Element;
 
 /**
@@ -216,4 +225,106 @@ public class SAMLParserTest {
             assertThat(parsedObject, instanceOf(ResponseType.class));
         }
     }
+
+    @Test
+    public void testSaml20AssertionsAnyTypeAttributeValue() throws Exception {
+
+        String[] xmlSamples = {
+                "saml20-assertion-anytype-attribute-value.xml",
+                "saml20-assertion-example.xml"
+        };
+
+        for (String fileName: xmlSamples) {
+            try (InputStream st = SAMLParserTest.class.getResourceAsStream(fileName)) {
+                Object parsedObject = parser.parse(st);
+                assertThat("Problem detected in " + fileName + " sample.", parsedObject, instanceOf(AssertionType.class));
+                checkCheckParsedResult(fileName, (AssertionType)parsedObject);
+            } catch (Exception e) {
+                throw new Exception("Problem detected in " + fileName + " sample.", e);
+            }
+        }
+    }
+
+    private void checkCheckParsedResult(String fileName, AssertionType assertion) throws Exception {
+        AttributeStatementType attributeStatementType = assertion.getAttributeStatements().iterator().next();
+        if ("saml20-assertion-anytype-attribute-value.xml".equals(fileName)) {
+            assertTrue("There has to be 3 attributes", attributeStatementType.getAttributes().size() == 3);
+            for (AttributeStatementType.ASTChoiceType choiceType: attributeStatementType.getAttributes()) {
+                AttributeType attr = choiceType.getAttribute();
+                String attrName = attr.getName();
+                String attrValueStatement = "unexpected value of attribute " + attrName + " of " + fileName;
+                String attrTypeStatement = "unexpected type of attribute " + attrName + " of " + fileName;
+                // test selected attributes
+                if (attrName.equals("attr:type:string")) {
+                    assertEquals(attrValueStatement, attr.getAttributeValue().get(0), "CITIZEN");
+                } else if (attrName.equals("attr:notype:string")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertEquals(attrValueStatement, value, "CITIZEN");
+                } else if (attrName.equals("attr:notype:element")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertThat(attrValueStatement, value, containsString("hospitaal x"));
+                    value = (String)attr.getAttributeValue().get(1);
+                    assertThat(attrValueStatement, value, containsString("hopital x"));
+                }
+            }
+        } else if ("saml20-assertion-example.xml".equals(fileName)) {
+            assertThat("There has to be 9 attributes", attributeStatementType.getAttributes().size(), is(9));
+            for (AttributeStatementType.ASTChoiceType choiceType: attributeStatementType.getAttributes()) {
+                AttributeType attr = choiceType.getAttribute();
+                String attrName = attr.getName();
+                String attrValueStatement = "unexpected value of attribute " + attrName + " of " + fileName;
+                String attrTypeStatement = "unexpected type of attribute " + attrName + " of " + fileName;
+                // test selected attributes
+                if (attrName.equals("portal_id")) {
+                    assertEquals(attrValueStatement, attr.getAttributeValue().get(0), "060D00000000SHZ");
+                } else if (attrName.equals("organization_id")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertThat(attrValueStatement, value, containsString("<n3:stuff xmlns:n3=\"ftp://example.org\">00DD0000000F7L5</n3:stuff>"));
+                } else if (attrName.equals("has_sub_organization")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertThat(attrValueStatement, value, containsString("true"));
+                } else if (attrName.equals("anytype_test")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertThat(attrValueStatement, value, containsString("<elem2>val2</elem2>"));
+                } else if (attrName.equals("anytype_no_xml_test")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertEquals(attrValueStatement, value, "value_no_xml");
+                } else if (attrName.equals("logouturl")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertEquals(attrValueStatement, value, "http://www.salesforce.com/security/del_auth/SsoLogoutPage.html");
+                } else if (attrName.equals("nil_value_attribute")) {
+                    assertNull(attrValueStatement, attr.getAttributeValue().get(0));
+                } else if (attrName.equals("status")) {
+                    assertThat(attrTypeStatement, attr.getAttributeValue().get(0), instanceOf(String.class));
+                    String value = (String)attr.getAttributeValue().get(0);
+                    assertThat(attrValueStatement, value, containsString("<status><code><status>XYZ</status></code></status>"));
+                }
+            }
+        } else {
+            throw new RuntimeException("test error: wrong file name to check");
+        }
+    }
+
+    @Test(expected = ParsingException.class)
+    public void testSaml20AssertionsNil1() throws IOException, ParsingException {
+        try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-assertion-nil-wrong-1.xml")) {
+            parser.parse(st);
+        }
+    }
+
+    @Test(expected = ParsingException.class)
+    public void testSaml20AssertionsNil2() throws IOException, ParsingException {
+        try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-assertion-nil-wrong-2.xml")) {
+            parser.parse(st);
+        }
+    }
+
+
 }
diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-anytype-attribute-value.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-anytype-attribute-value.xml
new file mode 100644
index 0000000..f4b7886
--- /dev/null
+++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-anytype-attribute-value.xml
@@ -0,0 +1,19 @@
+<saml2:Assertion ID="_2fca2bd7a8a8d472d2d35a0d0d75f896" IssueInstant="2017-02-01T15:46:55.938Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">issuer</saml2:Issuer>
+    <saml2:AttributeStatement>
+        <saml2:Attribute Name="attr:type:string" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">CITIZEN</saml2:AttributeValue>
+        </saml2:Attribute>
+        <saml2:Attribute Name="attr:notype:string" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">CITIZEN</saml2:AttributeValue>
+        </saml2:Attribute>
+        <saml2:Attribute Name="attr:notype:element" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+                <ns4:Name xml:lang="nl" xmlns="urn:be:fgov:ehealth:aa:complextype:v1" xmlns:ns4="urn:be:fgov:ehealth:aa:complextype:v1">hospitaal x</ns4:Name>
+            </saml2:AttributeValue>
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+                <ns4:Name xml:lang="fr" xmlns="urn:be:fgov:ehealth:aa:complextype:v1" xmlns:ns4="urn:be:fgov:ehealth:aa:complextype:v1">hopital x</ns4:Name>
+            </saml2:AttributeValue>
+        </saml2:Attribute>
+    </saml2:AttributeStatement>
+</saml2:Assertion>
diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-example.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-example.xml
new file mode 100644
index 0000000..591932e
--- /dev/null
+++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-example.xml
@@ -0,0 +1,132 @@
+<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                ID="_3c39bc0fe7b13769cab2f6f45eba801b1245264310738"
+                IssueInstant="2009-06-17T18:45:10.738Z" Version="2.0">
+    <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">
+        https://www.salesforce.com
+    </saml:Issuer>
+
+    <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
+        <SignedInfo>
+            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
+            <Reference URI="#_3c39bc0fe7b13769cab2f6f45eba801b1245264310738">
+                <Transforms>
+                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
+                    <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
+                        <ec:InclusiveNamespaces PrefixList="ds saml xs" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
+                    </Transform>
+                </Transforms>
+                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
+                <DigestValue>vzR9Hfp8d16576tEDeq/zhpmLoo=
+                </DigestValue>
+            </Reference>
+        </SignedInfo>
+        <SignatureValue>
+            AzID5hhJeJlG2llUDvZswNUrlrPtR7S37QYH2W+Un1n8c6kTC
+            Xr/lihEKPcA2PZt86eBntFBVDWTRlh/W3yUgGOqQBJMFOVbhK
+            M/CbLHbBUVT5TcxIqvsNvIFdjIGNkf1W0SBqRKZOJ6tzxCcLo
+            9dXqAyAUkqDpX5+AyltwrdCPNmncUM4dtRPjI05CL1rRaGeyX
+            3kkqOL8p0vjm0fazU5tCAJLbYuYgU1LivPSahWNcpvRSlCI4e
+            Pn2oiVDyrcc4et12inPMTc2lGIWWWWJyHOPSiXRSkEAIwQVjf
+            Qm5cpli44Pv8FCrdGWpEE0yXsPBvDkM9jIzwCYGG2fKaLBag==
+        </SignatureValue>
+        <KeyInfo>
+            <X509Data>
+                <X509Certificate>
+                    MIIEATCCAumgAwIBAgIBBTANBgkqhkiG9w0BAQ0FADCBgzELM
+                    [Certificate truncated for readability...]
+                </X509Certificate>
+            </X509Data>
+        </KeyInfo>
+    </Signature>
+
+    <saml:Subject>
+        <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">
+            saml01@salesforce.com
+        </saml:NameID>
+
+        <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
+            <saml:SubjectConfirmationData NotOnOrAfter="2009-06-17T18:50:10.738Z"
+                                          Recipient="https://login.salesforce.com"/>
+        </saml:SubjectConfirmation>
+    </saml:Subject>
+
+    <saml:Conditions NotBefore="2009-06-17T18:45:10.738Z"
+                     NotOnOrAfter="2009-06-17T18:50:10.738Z">
+
+        <saml:AudienceRestriction>
+            <saml:Audience>https://saml.salesforce.com</saml:Audience>
+        </saml:AudienceRestriction>
+    </saml:Conditions>
+
+    <saml:AuthnStatement AuthnInstant="2009-06-17T18:45:10.738Z">
+        <saml:AuthnContext>
+            <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified
+            </saml:AuthnContextClassRef>
+        </saml:AuthnContext>
+    </saml:AuthnStatement>
+
+    <saml:AttributeStatement>
+
+        <saml:Attribute Name="portal_id">
+            <saml:AttributeValue xsi:type="xs:anyType">060D00000000SHZ
+            </saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="organization_id">
+            <saml:AttributeValue xsi:type="xs:anyType">
+                <n1:elem2 xmlns:n1="http://example.net" xml:lang="en">
+                    <n3:stuff xmlns:n3="ftp://example.org">00DD0000000F7L5</n3:stuff>
+                </n1:elem2>
+            </saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="status">
+            <saml:AttributeValue xsi:type="xs:anyType">
+                <status>
+                    <code>
+                        <status>XYZ</status>
+                    </code>
+                </status>
+            </saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="has_sub_organization">
+            <saml:AttributeValue xsi:type="xs:boolean">true</saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="anytype_test">
+            <saml:AttributeValue>
+                <elem1 atttr1="en">
+                    <elem2>val2</elem2>
+                </elem1>
+            </saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="anytype_no_xml_test">
+            <saml:AttributeValue>value_no_xml</saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="ssostartpage"
+                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
+
+            <saml:AttributeValue xsi:type="xs:anyType">
+                http://www.salesforce.com/security/saml/saml20-gen.jsp
+            </saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="logouturl"
+                        NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+
+            <saml:AttributeValue xsi:type="xs:string">
+                http://www.salesforce.com/security/del_auth/SsoLogoutPage.html
+            </saml:AttributeValue>
+        </saml:Attribute>
+
+        <saml:Attribute Name="nil_value_attribute">
+            <saml:AttributeValue xsi:nil="true" xsi:type="xs:anyType"/>
+        </saml:Attribute>
+
+
+    </saml:AttributeStatement>
+</saml:Assertion>
diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-nil-wrong-1.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-nil-wrong-1.xml
new file mode 100644
index 0000000..1ce8a3c
--- /dev/null
+++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-nil-wrong-1.xml
@@ -0,0 +1,11 @@
+<saml2:Assertion ID="_2fca2bd7a8a8d472d2d35a0d0d75f896" IssueInstant="2017-02-01T15:46:55.938Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">issuer</saml2:Issuer>
+    <saml2:AttributeStatement>
+        <saml2:Attribute Name="attr:type:string" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">CITIZEN</saml2:AttributeValue>
+        </saml2:Attribute>
+        <saml2:Attribute Name="wrong:nil">
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="false" xsi:type="xs:anyType"/>
+        </saml2:Attribute>
+    </saml2:AttributeStatement>
+</saml2:Assertion>
diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-nil-wrong-2.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-nil-wrong-2.xml
new file mode 100644
index 0000000..e914d3a
--- /dev/null
+++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-nil-wrong-2.xml
@@ -0,0 +1,11 @@
+<saml2:Assertion ID="_2fca2bd7a8a8d472d2d35a0d0d75f896" IssueInstant="2017-02-01T15:46:55.938Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">issuer</saml2:Issuer>
+    <saml2:AttributeStatement>
+        <saml2:Attribute Name="attr:type:string" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">CITIZEN</saml2:AttributeValue>
+        </saml2:Attribute>
+        <saml2:Attribute Name="wrong:nil">
+            <saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="0" xsi:type="xs:anyType"/>
+        </saml2:Attribute>
+    </saml2:AttributeStatement>
+</saml2:Assertion>