diff --git a/saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/ExtensionsType.java b/saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/ExtensionsType.java index 364c90ec01..3420167779 100755 --- a/saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/ExtensionsType.java +++ b/saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/ExtensionsType.java @@ -18,6 +18,10 @@ package org.keycloak.dom.saml.v2.metadata; import org.w3c.dom.Element; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** *

* Java class for ExtensionsType complex type. @@ -39,13 +43,49 @@ import org.w3c.dom.Element; */ public class ExtensionsType { - protected Element element = null; + protected List any = new ArrayList(); + /** + * Function is obsoleted with getAny + * @return + */ + @Deprecated public Element getElement() { - return element; + return (any.isEmpty()) ? null : (Element) any.get(0); } + /** + * Function is obsoleted with addExtension + * @return + */ + @Deprecated public void setElement(Element element) { - this.element = element; + any.clear(); + any.add(element); + } + + /** + * Add an extension + * + * @param extension + */ + public void addExtension(Object extension) { + any.add(extension); + } + + /** + * Remove an extension + * + * @param extension + */ + public void removeExtension(Object extension) { + any.remove(extension); + } + + /** + * Gets the value of the any property. + */ + public List getAny() { + return Collections.unmodifiableList(this.any); } } \ No newline at end of file diff --git a/saml-core-api/src/main/java/org/keycloak/dom/xmlsec/w3/xmlenc/EncryptionMethodType.java b/saml-core-api/src/main/java/org/keycloak/dom/xmlsec/w3/xmlenc/EncryptionMethodType.java index a88273de06..2d3e82b8aa 100755 --- a/saml-core-api/src/main/java/org/keycloak/dom/xmlsec/w3/xmlenc/EncryptionMethodType.java +++ b/saml-core-api/src/main/java/org/keycloak/dom/xmlsec/w3/xmlenc/EncryptionMethodType.java @@ -56,6 +56,11 @@ public class EncryptionMethodType { OAEPparams = oAEPparams; } + public EncryptionMethod() { + this.keySize = null; + this.OAEPparams = null; + } + public BigInteger getKeySize() { return keySize; } @@ -63,6 +68,14 @@ public class EncryptionMethodType { public byte[] getOAEPparams() { return OAEPparams; } + + public void setKeySize(BigInteger keySize) { + this.keySize = keySize; + } + + public void setOAEPparams(byte[] OAEPparams) { + this.OAEPparams = OAEPparams; + } } public EncryptionMethodType(String algo) { diff --git a/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java b/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java index 4f8a9a8614..aee809104a 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java @@ -32,6 +32,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.xml.XMLConstants; +import javax.xml.datatype.Duration; import javax.xml.namespace.QName; import javax.xml.stream.Location; import javax.xml.stream.XMLEventReader; @@ -52,7 +53,10 @@ import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import java.io.InputStream; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; +import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.datatype.XMLGregorianCalendar; @@ -288,6 +292,20 @@ public class StaxParserUtil { return value == null ? null : XMLTimeUtil.parse(value); } + /** + * Get the Attribute value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static Duration getXmlDurationAttributeValue(StartElement startElement, HasQName attrName) throws ParsingException { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValue(attr); + return value == null ? null : XMLTimeUtil.parseAsDuration(value); + } + /** * Get the Attribute value * @@ -391,6 +409,29 @@ public class StaxParserUtil { return StaxParserUtil.getAttributeValueRP(attr); } + /** + * Parse a space delimited list of strings + * + * @param startElement + * @param attrName + * + * @return + */ + public static List getRequiredStringListAttributeValue(StartElement startElement, HasQName attrName) throws ParsingException{ + List protocolEnum = new ArrayList<>(); + + String val = StaxParserUtil.getRequiredAttributeValue(startElement, attrName); + if (StringUtil.isNotNull(val)) { + StringTokenizer st = new StringTokenizer(val); + while (st.hasMoreTokens()) { + protocolEnum.add(st.nextToken()); + } + + } + + return protocolEnum; + } + private static final String JDK_TRANSFORMER_PROPERTY = "picketlink.jdk.transformer"; /** diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParser.java index b4695473f1..52599a1e67 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParser.java @@ -16,6 +16,7 @@ */ package org.keycloak.saml.processing.core.parsers.saml; +import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames; import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLAttributeQueryParser; import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLSloRequestParser; import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLSloResponseParser; @@ -53,8 +54,6 @@ import java.io.InputStream; */ public class SAMLParser extends AbstractParser { - private static final SAMLEntityDescriptorParser SAML_ENTITY_DESCRIPTOR_PARSER = new SAMLEntityDescriptorParser(); - private static final SAMLEntitiesDescriptorParser SAML_ENTITIES_DESCRIPTOR_PARSER = new SAMLEntitiesDescriptorParser(); private static final SAML11ResponseParser SAML_11_RESPONSE_PARSER = new SAML11ResponseParser(); private static final SAML11RequestParser SAML_11_REQUEST_PARSER = new SAML11RequestParser(); @@ -88,9 +87,8 @@ public class SAMLParser extends AbstractParser { PARSERS.put(SAMLAssertionQNames.AUTHN_STATEMENT.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLAuthnStatementParser.getInstance(); }}); - // TODO: Change to SamlMetadataElements - PARSERS.put(JBossSAMLConstants.ENTITY_DESCRIPTOR.getAsQName(), new ParserFactory() { @Override public StaxParser create() { return SAML_ENTITY_DESCRIPTOR_PARSER; }}); - PARSERS.put(JBossSAMLConstants.ENTITIES_DESCRIPTOR.getAsQName(),new ParserFactory() { @Override public StaxParser create() { return SAML_ENTITIES_DESCRIPTOR_PARSER; }}); + PARSERS.put(SAMLMetadataQNames.ENTITY_DESCRIPTOR.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLEntityDescriptorParser.getInstance(); }}); + PARSERS.put(SAMLMetadataQNames.ENTITIES_DESCRIPTOR.getQName(),new ParserFactory() { @Override public StaxParser create() { return SAMLEntitiesDescriptorParser.getInstance(); }}); PARSERS.put(SAMLProtocolQNames.ATTRIBUTE_QUERY.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLAttributeQueryParser.getInstance(); }}); } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlMetadataParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlMetadataParser.java new file mode 100644 index 0000000000..9d9122a8f3 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlMetadataParser.java @@ -0,0 +1,24 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.saml.common.parsers.AbstractStaxParser; +import org.keycloak.saml.processing.core.parsers.util.QNameEnumLookup; + +import javax.xml.namespace.QName; + +/** + * @author mhajas + */ +abstract public class AbstractStaxSamlMetadataParser extends AbstractStaxParser { + + protected static final QNameEnumLookup LOOKUP = new QNameEnumLookup(SAMLMetadataQNames.values()); + + + public AbstractStaxSamlMetadataParser(SAMLMetadataQNames expectedStartElement) { + super(expectedStartElement.getQName(), SAMLMetadataQNames.UNKNOWN_ELEMENT); + } + + @Override + protected SAMLMetadataQNames getElementFromName(QName name) { + return LOOKUP.from(name); + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlParser.java deleted file mode 100644 index c43754c445..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlParser.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.saml.processing.core.parsers.saml.metadata; - -import org.keycloak.saml.common.constants.JBossSAMLConstants; -import javax.xml.namespace.QName; -import org.keycloak.saml.common.parsers.AbstractStaxParser; - -/** - * - * @author hmlnarik - */ -public abstract class AbstractStaxSamlParser extends AbstractStaxParser { - - public AbstractStaxSamlParser(JBossSAMLConstants expectedStartElement) { - super(expectedStartElement.getAsQName(), JBossSAMLConstants.UNKNOWN_VALUE); - } - - @Override - protected boolean isUnknownElement(JBossSAMLConstants token) { - return token == JBossSAMLConstants.UNKNOWN_VALUE; - } - - @Override - protected JBossSAMLConstants getElementFromName(QName name) { - JBossSAMLConstants res = JBossSAMLConstants.from(name); - - if ((res == null || res == JBossSAMLConstants.UNKNOWN_VALUE) && name != null) { - // Relax and search regardless of namespace - res = JBossSAMLConstants.from(name.getLocalPart()); - } - - return res; - } - -} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLArtifactResolutionServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLArtifactResolutionServiceParser.java new file mode 100644 index 0000000000..45ba48806d --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLArtifactResolutionServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLArtifactResolutionServiceParser extends SAMLIndexedEndpointTypeParser { + + private static final SAMLArtifactResolutionServiceParser INSTANCE = new SAMLArtifactResolutionServiceParser(); + + public SAMLArtifactResolutionServiceParser() { + super(SAMLMetadataQNames.ARTIFACT_RESOLUTION_SERVICE); + } + + public static SAMLArtifactResolutionServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAssertinIDRequestServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAssertinIDRequestServiceParser.java new file mode 100644 index 0000000000..a7d7beefb8 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAssertinIDRequestServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLAssertinIDRequestServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLAssertinIDRequestServiceParser INSTANCE = new SAMLAssertinIDRequestServiceParser(); + + public SAMLAssertinIDRequestServiceParser() { + super(SAMLMetadataQNames.ASSERTION_ID_REQUEST_SERVICE); + } + + public static SAMLAssertinIDRequestServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAssertionConsumerServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAssertionConsumerServiceParser.java new file mode 100644 index 0000000000..28b54e4b8c --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAssertionConsumerServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLAssertionConsumerServiceParser extends SAMLIndexedEndpointTypeParser { + + private static final SAMLAssertionConsumerServiceParser INSTANCE = new SAMLAssertionConsumerServiceParser(); + + public SAMLAssertionConsumerServiceParser() { + super(SAMLMetadataQNames.ASSERTION_CONSUMER_SERVICE); + } + + public static SAMLAssertionConsumerServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeAuthorityDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeAuthorityDescriptorParser.java new file mode 100644 index 0000000000..0a1a83ea32 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeAuthorityDescriptorParser.java @@ -0,0 +1,65 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.AttributeAuthorityDescriptorType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.util.List; + +/** + * @author mhajas + */ +public class SAMLAttributeAuthorityDescriptorParser extends SAMLRoleDecriptorTypeParser { + + private static final SAMLAttributeAuthorityDescriptorParser INSTANCE = new SAMLAttributeAuthorityDescriptorParser(); + + public SAMLAttributeAuthorityDescriptorParser() { + super(SAMLMetadataQNames.ATTRIBUTE_AUTHORITY_DESCRIPTOR); + } + + public static SAMLAttributeAuthorityDescriptorParser getInstance() { + return INSTANCE; + } + + @Override + protected AttributeAuthorityDescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + List protocolEnum = StaxParserUtil.getRequiredStringListAttributeValue(element, SAMLMetadataQNames.ATTR_PROTOCOL_SUPPORT_ENUMERATION); + AttributeAuthorityDescriptorType descriptor = new AttributeAuthorityDescriptorType(protocolEnum); + + parseOptionalArguments(element, descriptor); + + return descriptor; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AttributeAuthorityDescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ATTRIBUTE_SERVICE: + target.addAttributeService(SAMLAttributeServiceParser.getInstance().parse(xmlEventReader)); + break; + + case ASSERTION_ID_REQUEST_SERVICE: + target.addAssertionIDRequestService(SAMLAssertinIDRequestServiceParser.getInstance().parse(xmlEventReader)); + break; + + case NAMEID_FORMAT: + StaxParserUtil.advance(xmlEventReader); + target.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case ATTRIBUTE_PROFILE: + StaxParserUtil.advance(xmlEventReader); + target.addAttributeProfile(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case ATTRIBUTE: + target.addAttribute(SAMLAttributeParser.getInstance().parse(xmlEventReader)); + break; + + default: + super.processSubElement(xmlEventReader, target, element, elementDetail); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeConsumingServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeConsumingServiceParser.java new file mode 100644 index 0000000000..6bdded981b --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeConsumingServiceParser.java @@ -0,0 +1,61 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.AttributeConsumingServiceType; +import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.ATTR_LANG; + + +/** + * @author mhajas + */ +public class SAMLAttributeConsumingServiceParser extends AbstractStaxSamlMetadataParser { + + private static final SAMLAttributeConsumingServiceParser INSTANCE = new SAMLAttributeConsumingServiceParser(); + + public SAMLAttributeConsumingServiceParser() { + super(SAMLMetadataQNames.ATTRIBUTE_CONSUMING_SERVICE); + } + + public static SAMLAttributeConsumingServiceParser getInstance() { + return INSTANCE; + } + + @Override + protected AttributeConsumingServiceType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + int index = Integer.parseInt(StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_INDEX)); + + AttributeConsumingServiceType service = new AttributeConsumingServiceType(index); + service.setIsDefault(StaxParserUtil.getBooleanAttributeValue(element, SAMLMetadataQNames.ATTR_IS_DEFAULT)); + + return service; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AttributeConsumingServiceType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case SERVICE_NAME: + LocalizedNameType serviceName = new LocalizedNameType(StaxParserUtil.getAttributeValue(elementDetail, ATTR_LANG)); + StaxParserUtil.advance(xmlEventReader); + serviceName.setValue(StaxParserUtil.getElementText(xmlEventReader)); + target.addServiceName(serviceName); + break; + + case SERVICE_DESCRIPTION: + target.addServiceDescription(new LocalizedNameType(StaxParserUtil.getAttributeValue(elementDetail, ATTR_LANG))); + break; + + case REQUESTED_ATTRIBUTE: + target.addRequestedAttribute(SAMLRequestedAttributeParser.getInstance().parse(xmlEventReader)); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeParser.java new file mode 100644 index 0000000000..0b719a292d --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeParser.java @@ -0,0 +1,71 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.assertion.AttributeType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeValueParser; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLAttributeParser extends AbstractStaxSamlMetadataParser { + + private static final SAMLAttributeParser INSTANCE = new SAMLAttributeParser(); + + private SAMLAttributeParser() { + super(SAMLMetadataQNames.ATTRIBUTE); + } + + public static SAMLAttributeParser getInstance() { + return INSTANCE; + } + + @Override + protected AttributeType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + String name = StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_NAME); + final AttributeType attribute = new AttributeType(name); + + attribute.setFriendlyName(StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_FRIENDLY_NAME)); + attribute.setNameFormat(StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_NAME_FORMAT)); + + final String x500Encoding = StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_X500_ENCODING); + if (x500Encoding != null) { + attribute.getOtherAttributes().put(SAMLMetadataQNames.ATTR_X500_ENCODING.getQName(), x500Encoding); + } + + return attribute; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AttributeType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ATTRIBUTE_VALUE: + target.addAttributeValue(SAMLAttributeValueParser.getInstance().parse(xmlEventReader)); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } +} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeServiceParser.java new file mode 100644 index 0000000000..a8d15cd4a2 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLAttributeServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLAttributeServiceParser INSTANCE = new SAMLAttributeServiceParser(); + + public SAMLAttributeServiceParser() { + super(SAMLMetadataQNames.ATTRIBUTE_SERVICE); + } + + public static SAMLAttributeServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthnAuthorityDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthnAuthorityDescriptorParser.java new file mode 100644 index 0000000000..f67620687e --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthnAuthorityDescriptorParser.java @@ -0,0 +1,56 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.AuthnAuthorityDescriptorType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.util.List; + +/** + * @author mhajas + */ +public class SAMLAuthnAuthorityDescriptorParser extends SAMLRoleDecriptorTypeParser { + + private static final SAMLAuthnAuthorityDescriptorParser INSTANCE = new SAMLAuthnAuthorityDescriptorParser(); + + public SAMLAuthnAuthorityDescriptorParser() { + super(SAMLMetadataQNames.AUTHN_AUTHORITY_DESCRIPTOR); + } + + public static SAMLAuthnAuthorityDescriptorParser getInstance() { + return INSTANCE; + } + + @Override + protected AuthnAuthorityDescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + List protocolEnum = StaxParserUtil.getRequiredStringListAttributeValue(element, SAMLMetadataQNames.ATTR_PROTOCOL_SUPPORT_ENUMERATION); + AuthnAuthorityDescriptorType descriptor = new AuthnAuthorityDescriptorType(protocolEnum); + + parseOptionalArguments(element, descriptor); + + return descriptor; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AuthnAuthorityDescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case AUTHN_QUERY_SERVICE: + target.addAuthnQueryService(SAMLAuthnQueryServiceParser.getInstance().parse(xmlEventReader)); + break; + + case ASSERTION_ID_REQUEST_SERVICE: + target.addAssertionIDRequestService(SAMLAssertinIDRequestServiceParser.getInstance().parse(xmlEventReader)); + break; + + case NAMEID_FORMAT: + StaxParserUtil.advance(xmlEventReader); + target.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); + break; + + default: + super.processSubElement(xmlEventReader, target, element, elementDetail); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthnQueryServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthnQueryServiceParser.java new file mode 100644 index 0000000000..9f10ce45d2 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthnQueryServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLAuthnQueryServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLAuthnQueryServiceParser INSTANCE = new SAMLAuthnQueryServiceParser(); + + public SAMLAuthnQueryServiceParser() { + super(SAMLMetadataQNames.AUTHN_QUERY_SERVICE); + } + + public static SAMLAuthnQueryServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthzServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthzServiceParser.java new file mode 100644 index 0000000000..1c489a3a50 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAuthzServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLAuthzServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLAuthzServiceParser INSTANCE = new SAMLAuthzServiceParser(); + + public SAMLAuthzServiceParser() { + super(SAMLMetadataQNames.AUTHZ_SERVICE); + } + + public static SAMLAuthzServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLContactPersonParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLContactPersonParser.java new file mode 100644 index 0000000000..aa16eb336b --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLContactPersonParser.java @@ -0,0 +1,70 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.ContactType; +import org.keycloak.dom.saml.v2.metadata.ContactTypeType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.ATTR_CONTACT_TYPE; +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.CONTACT_PERSON; + +/** + * @author mhajas + */ +public class SAMLContactPersonParser extends AbstractStaxSamlMetadataParser { + + private static final SAMLContactPersonParser INSTANCE = new SAMLContactPersonParser(); + + public SAMLContactPersonParser() { + super(CONTACT_PERSON); + } + + public static SAMLContactPersonParser getInstance() { + return INSTANCE; + } + + @Override + protected ContactType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new ContactType(ContactTypeType.fromValue(StaxParserUtil.getRequiredAttributeValue(element, ATTR_CONTACT_TYPE))); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, ContactType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case COMPANY: + StaxParserUtil.advance(xmlEventReader); + target.setCompany(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case GIVEN_NAME: + StaxParserUtil.advance(xmlEventReader); + target.setGivenName(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case SURNAME: + StaxParserUtil.advance(xmlEventReader); + target.setSurName(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case EMAIL_ADDRESS: + StaxParserUtil.advance(xmlEventReader); + target.addEmailAddress(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case TELEPHONE_NUMBER: + StaxParserUtil.advance(xmlEventReader); + target.addTelephone(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case EXTENSIONS: + target.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEncryptionMethodParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEncryptionMethodParser.java new file mode 100644 index 0000000000..70dec2a836 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEncryptionMethodParser.java @@ -0,0 +1,70 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.xmlsec.w3.xmlenc.EncryptionMethodType; +import org.keycloak.saml.common.constants.GeneralConstants; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.math.BigInteger; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.ENCRYPTION_METHOD; + +/** + * @author mhajas + */ +public class SAMLEncryptionMethodParser extends AbstractStaxSamlMetadataParser { + + private static final SAMLEncryptionMethodParser INSTANCE = new SAMLEncryptionMethodParser(); + + public SAMLEncryptionMethodParser() { + super(ENCRYPTION_METHOD); + } + + public static SAMLEncryptionMethodParser getInstance() { + return INSTANCE; + } + + @Override + protected EncryptionMethodType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new EncryptionMethodType(StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_ALGORITHM)); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, EncryptionMethodType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch(element) { + case KEY_SIZE: + { + StaxParserUtil.advance(xmlEventReader); + BigInteger keySize = BigInteger.valueOf(Long.valueOf(StaxParserUtil.getElementText(xmlEventReader))); + + EncryptionMethodType.EncryptionMethod encMethod = target.getEncryptionMethod(); + if (encMethod == null) { + encMethod = new EncryptionMethodType.EncryptionMethod(); + target.setEncryptionMethod(encMethod); + } + + encMethod.setKeySize(keySize); + } + break; + + case OAEP_PARAMS: + { + StaxParserUtil.advance(xmlEventReader); + byte[] OAEPparams = StaxParserUtil.getElementText(xmlEventReader).getBytes(GeneralConstants.SAML_CHARSET); + EncryptionMethodType.EncryptionMethod encMethod = target.getEncryptionMethod(); + if (encMethod == null){ + encMethod = new EncryptionMethodType.EncryptionMethod(); + target.setEncryptionMethod(encMethod); + } + + encMethod.setOAEPparams(OAEPparams); + } + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEndpointTypeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEndpointTypeParser.java new file mode 100644 index 0000000000..d0b276ec89 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEndpointTypeParser.java @@ -0,0 +1,40 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.EndpointType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.net.URI; + +/** + * @author mhajas + */ +public abstract class SAMLEndpointTypeParser extends AbstractStaxSamlMetadataParser { + + public SAMLEndpointTypeParser(SAMLMetadataQNames expectedStartElement) { + super(expectedStartElement); + } + + @Override + protected EndpointType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + String binding = StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_BINDING); + String location = StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_LOCATION); + + EndpointType endpoint = new EndpointType(URI.create(binding), URI.create(location)); + + String responseLocation = StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_RESPONSE_LOCATION); + + if (responseLocation != null) { + endpoint.setResponseLocation(URI.create(responseLocation)); + } + + return endpoint; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, EndpointType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntitiesDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntitiesDescriptorParser.java index 83e9179986..56a1c34c00 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntitiesDescriptorParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntitiesDescriptorParser.java @@ -17,24 +17,12 @@ package org.keycloak.saml.processing.core.parsers.saml.metadata; import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType; -import org.keycloak.dom.saml.v2.metadata.ExtensionsType; -import org.keycloak.saml.common.PicketLinkLogger; -import org.keycloak.saml.common.PicketLinkLoggerFactory; -import org.keycloak.saml.common.constants.JBossSAMLConstants; -import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Element; -import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; -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 org.keycloak.saml.common.parsers.StaxParser; /** * Parse the SAML Entities Descriptor @@ -42,74 +30,53 @@ import org.keycloak.saml.common.parsers.StaxParser; * @author Anil.Saldhana@redhat.com * @since Jan 31, 2011 */ -public class SAMLEntitiesDescriptorParser extends AbstractParser { +public class SAMLEntitiesDescriptorParser extends AbstractStaxSamlMetadataParser { - private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); + private static final SAMLEntitiesDescriptorParser INSTANCE = new SAMLEntitiesDescriptorParser(); - private static final String EDT = JBossSAMLConstants.ENTITIES_DESCRIPTOR.get(); + public SAMLEntitiesDescriptorParser() { + super(SAMLMetadataQNames.ENTITIES_DESCRIPTOR); + } - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { + public static SAMLEntitiesDescriptorParser getInstance() { + return INSTANCE; + } - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, EDT); - - EntitiesDescriptorType entitiesDescriptorType = new EntitiesDescriptorType(); + @Override + protected EntitiesDescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + EntitiesDescriptorType descriptor = new EntitiesDescriptorType(); // Parse the attributes - Attribute validUntil = startElement.getAttributeByName(new QName(JBossSAMLConstants.VALID_UNTIL.get())); - if (validUntil != null) { - String validUntilValue = StaxParserUtil.getAttributeValue(validUntil); - entitiesDescriptorType.setValidUntil(XMLTimeUtil.parse(validUntilValue)); - } + descriptor.setID(StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_ID)); + descriptor.setValidUntil(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLMetadataQNames.ATTR_VALID_UNTIL)); + descriptor.setCacheDuration(StaxParserUtil.getXmlDurationAttributeValue(element, SAMLMetadataQNames.ATTR_CACHE_DURATION)); + descriptor.setName(StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_NAME)); - Attribute id = startElement.getAttributeByName(new QName(JBossSAMLConstants.ID.get())); - if (id != null) { - entitiesDescriptorType.setID(StaxParserUtil.getAttributeValue(id)); - } - - Attribute name = startElement.getAttributeByName(new QName(JBossSAMLConstants.NAME.get())); - if (name != null) { - entitiesDescriptorType.setName(StaxParserUtil.getAttributeValue(name)); - } - - Attribute cacheDuration = startElement.getAttributeByName(new QName(JBossSAMLConstants.CACHE_DURATION.get())); - if (cacheDuration != null) { - entitiesDescriptorType - .setCacheDuration(XMLTimeUtil.parseAsDuration(StaxParserUtil.getAttributeValue(cacheDuration))); - } - - // Get the Child Elements - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - StaxParserUtil.validate((EndElement) xmlEvent, EDT); - StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); - - if (JBossSAMLConstants.ENTITY_DESCRIPTOR.get().equals(localPart)) { - SAMLEntityDescriptorParser entityParser = new SAMLEntityDescriptorParser(); - entitiesDescriptorType.addEntityDescriptor(entityParser.parse(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS__METADATA.get().equalsIgnoreCase(localPart)) { - entitiesDescriptorType.setExtensions(parseExtensions(xmlEventReader)); - } else if (JBossSAMLConstants.ENTITIES_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { - SAMLEntitiesDescriptorParser parser = new SAMLEntitiesDescriptorParser(); - entitiesDescriptorType.addEntityDescriptor(parser.parse(xmlEventReader)); - } else if (localPart.equals(JBossSAMLConstants.SIGNATURE.get())) { - entitiesDescriptorType.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - return entitiesDescriptorType; + return descriptor; } + @Override + protected void processSubElement(XMLEventReader xmlEventReader, EntitiesDescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case SIGNATURE: + Element sig = StaxParserUtil.getDOMElement(xmlEventReader); + target.setSignature(sig); + break; - private ExtensionsType parseExtensions(XMLEventReader xmlEventReader) throws ParsingException { - ExtensionsType extensions = new ExtensionsType(); - Element extElement = StaxParserUtil.getDOMElement(xmlEventReader); - extensions.setElement(extElement); - return extensions; + case EXTENSIONS: + target.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); + break; + + case ENTITY_DESCRIPTOR: + target.addEntityDescriptor(SAMLEntityDescriptorParser.getInstance().parse(xmlEventReader)); + break; + + case ENTITIES_DESCRIPTOR: + target.addEntityDescriptor(parse(xmlEventReader)); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntityDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntityDescriptorParser.java index f94027db61..9c4eb187d3 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntityDescriptorParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntityDescriptorParser.java @@ -16,50 +16,20 @@ */ package org.keycloak.saml.processing.core.parsers.saml.metadata; -import org.keycloak.dom.saml.v2.assertion.AttributeType; import org.keycloak.dom.saml.v2.metadata.AttributeAuthorityDescriptorType; -import org.keycloak.dom.saml.v2.metadata.AttributeConsumingServiceType; -import org.keycloak.dom.saml.v2.metadata.ContactType; -import org.keycloak.dom.saml.v2.metadata.ContactTypeType; -import org.keycloak.dom.saml.v2.metadata.EndpointType; +import org.keycloak.dom.saml.v2.metadata.AuthnAuthorityDescriptorType; import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; -import org.keycloak.dom.saml.v2.metadata.ExtensionsType; import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType; -import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType; -import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; -import org.keycloak.dom.saml.v2.metadata.KeyTypes; -import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; -import org.keycloak.dom.saml.v2.metadata.LocalizedURIType; -import org.keycloak.dom.saml.v2.metadata.OrganizationType; +import org.keycloak.dom.saml.v2.metadata.PDPDescriptorType; import org.keycloak.dom.saml.v2.metadata.RoleDescriptorType; import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; -import org.keycloak.dom.xmlsec.w3.xmlenc.EncryptionMethodType; -import org.keycloak.saml.common.PicketLinkLogger; -import org.keycloak.saml.common.PicketLinkLoggerFactory; -import org.keycloak.saml.common.constants.GeneralConstants; -import org.keycloak.saml.common.constants.JBossSAMLConstants; -import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.common.util.StringUtil; -import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeParser; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; -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.math.BigInteger; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; -import org.keycloak.saml.common.parsers.StaxParser; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.ENTITY_DESCRIPTOR; /** * Parse the SAML Metadata element "EntityDescriptor" @@ -67,621 +37,107 @@ import org.keycloak.saml.common.parsers.StaxParser; * @author Anil.Saldhana@redhat.com * @since Dec 14, 2010 */ -public class SAMLEntityDescriptorParser extends AbstractParser { +public class SAMLEntityDescriptorParser extends AbstractStaxSamlMetadataParser { - private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); + private static final SAMLEntityDescriptorParser INSTANCE = new SAMLEntityDescriptorParser(); - private static final String EDT = JBossSAMLConstants.ENTITY_DESCRIPTOR.get(); + private SAMLEntityDescriptorParser() { + super(ENTITY_DESCRIPTOR); + } - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { + public static SAMLEntityDescriptorParser getInstance() { + return INSTANCE; + } - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, EDT); + @Override + protected EntityDescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + String entityID = StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_ENTITY_ID); + EntityDescriptorType descriptor = new EntityDescriptorType(entityID); - Attribute entityID = startElement.getAttributeByName(new QName(JBossSAMLConstants.ENTITY_ID.get())); - String entityIDValue = StaxParserUtil.getAttributeValue(entityID); - EntityDescriptorType entityDescriptorType = new EntityDescriptorType(entityIDValue); + descriptor.setValidUntil(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLMetadataQNames.ATTR_VALID_UNTIL)); + descriptor.setCacheDuration(StaxParserUtil.getXmlDurationAttributeValue(element, SAMLMetadataQNames.ATTR_CACHE_DURATION)); + descriptor.setID(StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_ID)); - Attribute validUntil = startElement.getAttributeByName(new QName(JBossSAMLConstants.VALID_UNTIL.get())); - if (validUntil != null) { - String validUntilValue = StaxParserUtil.getAttributeValue(validUntil); - entityDescriptorType.setValidUntil(XMLTimeUtil.parse(validUntilValue)); - } + return descriptor; + } - Attribute id = startElement.getAttributeByName(new QName(JBossSAMLConstants.ID.get())); - if (id != null) { - entityDescriptorType.setID(StaxParserUtil.getAttributeValue(id)); - } - - Attribute cacheDuration = startElement.getAttributeByName(new QName(JBossSAMLConstants.CACHE_DURATION.get())); - if (cacheDuration != null) { - entityDescriptorType.setCacheDuration(XMLTimeUtil.parseAsDuration(StaxParserUtil.getAttributeValue(cacheDuration))); - } - - // Get the Child Elements - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - StaxParserUtil.validate((EndElement) xmlEvent, EDT); - StaxParserUtil.getNextEndElement(xmlEventReader); + @Override + protected void processSubElement(XMLEventReader xmlEventReader, EntityDescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case SIGNATURE: + target.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); - if (JBossSAMLConstants.IDP_SSO_DESCRIPTOR.get().equals(localPart)) { - IDPSSODescriptorType idpSSO = parseIDPSSODescriptor(xmlEventReader); - - EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(idpSSO); - EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); - entityDescriptorType.addChoiceType(edtChoice); - } else if (JBossSAMLConstants.SP_SSO_DESCRIPTOR.get().equals(localPart)) { - SPSSODescriptorType spSSO = parseSPSSODescriptor(xmlEventReader); - - EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(spSSO); - EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); - entityDescriptorType.addChoiceType(edtChoice); - } else if (JBossSAMLConstants.ATTRIBUTE_AUTHORITY_DESCRIPTOR.get().equals(localPart)) { - AttributeAuthorityDescriptorType attrAuthority = parseAttributeAuthorityDescriptor(xmlEventReader); - - EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(attrAuthority); - EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); - entityDescriptorType.addChoiceType(edtChoice); - } else if (JBossSAMLConstants.AUTHN_AUTHORITY_DESCRIPTOR.get().equals(localPart)) { - throw logger.unsupportedType("AuthnAuthorityDescriptor"); - } else if (JBossSAMLConstants.AFFILIATION_DESCRIPTOR.get().equals(localPart)) { - throw logger.unsupportedType(" AffiliationDescriptor"); - } else if (JBossSAMLConstants.PDP_DESCRIPTOR.get().equals(localPart)) { - throw logger.unsupportedType(" PDPDescriptor"); - } else if (localPart.equals(JBossSAMLConstants.SIGNATURE.get())) { - entityDescriptorType.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); - } else if (JBossSAMLConstants.ORGANIZATION.get().equals(localPart)) { - OrganizationType organization = parseOrganization(xmlEventReader); - - entityDescriptorType.setOrganization(organization); - } else if (JBossSAMLConstants.CONTACT_PERSON.get().equals(localPart)) { - entityDescriptorType.addContactPerson(parseContactPerson(xmlEventReader)); - } else if (JBossSAMLConstants.ADDITIONAL_METADATA_LOCATION.get().equals(localPart)) { - throw logger.unsupportedType("AdditionalMetadataLocation"); - } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { - entityDescriptorType.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); - } else if (JBossSAMLConstants.ROLE_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { - RoleDescriptorType roleDescriptor = parseRoleDescriptor(xmlEventReader); - - EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(roleDescriptor); - EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); - - entityDescriptorType.addChoiceType(edtChoice); - } else - throw logger.parserUnknownStartElement(localPart, startElement.getLocation()); - } - return entityDescriptorType; - } - - - private SPSSODescriptorType parseSPSSODescriptor(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.SP_SSO_DESCRIPTOR.get()); - - List protocolEnum = parseProtocolEnumeration(startElement); - SPSSODescriptorType spSSODescriptor = new SPSSODescriptorType(protocolEnum); - - Attribute wantAssertionsSigned = startElement.getAttributeByName(new QName(JBossSAMLConstants.WANT_ASSERTIONS_SIGNED - .get())); - if (wantAssertionsSigned != null) { - spSSODescriptor - .setWantAssertionsSigned(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(wantAssertionsSigned))); - } - Attribute wantAuthnSigned = startElement.getAttributeByName(new QName(JBossSAMLConstants.AUTHN_REQUESTS_SIGNED.get())); - if (wantAuthnSigned != null) { - spSSODescriptor.setAuthnRequestsSigned(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(wantAuthnSigned))); - } - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.SP_SSO_DESCRIPTOR.get()); + case EXTENSIONS: + target.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); + case IDP_SSO_DESCRIPTOR: + { + IDPSSODescriptorType idpSSO = SAMLIDPSSODescriptorParser.getInstance().parse(xmlEventReader); - if (JBossSAMLConstants.ARTIFACT_RESOLUTION_SERVICE.get().equals(localPart)) { - IndexedEndpointType endpoint = parseArtifactResolutionService(xmlEventReader, startElement); - spSSODescriptor.addArtifactResolutionService(endpoint); - } else if (JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE.get().equals(localPart)) { - IndexedEndpointType endpoint = parseAssertionConsumerService(xmlEventReader, startElement); - spSSODescriptor.addAssertionConsumerService(endpoint); - } else if (JBossSAMLConstants.ATTRIBUTE_CONSUMING_SERVICE.get().equals(localPart)) { - AttributeConsumingServiceType attributeConsumer = parseAttributeConsumingService(xmlEventReader, startElement); - spSSODescriptor.addAttributeConsumerService(attributeConsumer); - } else if (JBossSAMLConstants.SINGLE_LOGOUT_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.SINGLE_LOGOUT_SERVICE.get()); - - spSSODescriptor.addSingleLogoutService(endpoint); - } else if (JBossSAMLConstants.MANAGE_NAMEID_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.MANAGE_NAMEID_SERVICE.get()); - - spSSODescriptor.addManageNameIDService(endpoint); - } else if (JBossSAMLConstants.NAMEID_FORMAT.get().equalsIgnoreCase(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - spSSODescriptor.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.KEY_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { - spSSODescriptor.addKeyDescriptor(parseKeyDescriptor(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { - spSSODescriptor.setExtensions(parseExtensions(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - return spSSODescriptor; - } - - private IDPSSODescriptorType parseIDPSSODescriptor(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.IDP_SSO_DESCRIPTOR.get()); - - List protocolEnum = parseProtocolEnumeration(startElement); - IDPSSODescriptorType idpSSODescriptor = new IDPSSODescriptorType(protocolEnum); - - Attribute wantAuthnSigned = startElement.getAttributeByName(new QName(JBossSAMLConstants.WANT_AUTHN_REQUESTS_SIGNED - .get())); - if (wantAuthnSigned != null) { - idpSSODescriptor - .setWantAuthnRequestsSigned(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(wantAuthnSigned))); - } - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.IDP_SSO_DESCRIPTOR.get()); + EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(idpSSO); + EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); + target.addChoiceType(edtChoice); + } break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); + case SP_SSO_DESCRIPTOR: + { + SPSSODescriptorType spSSO = SAMLSPSSODescriptorParser.getInstance().parse(xmlEventReader); - if (JBossSAMLConstants.ARTIFACT_RESOLUTION_SERVICE.get().equals(localPart)) { - IndexedEndpointType endpoint = parseArtifactResolutionService(xmlEventReader, startElement); - idpSSODescriptor.addArtifactResolutionService(endpoint); - } else if (JBossSAMLConstants.ASSERTION_ID_REQUEST_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.ASSERTION_ID_REQUEST_SERVICE.get()); - - idpSSODescriptor.addAssertionIDRequestService(endpoint); - } else if (JBossSAMLConstants.SINGLE_LOGOUT_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.SINGLE_LOGOUT_SERVICE.get()); - - idpSSODescriptor.addSingleLogoutService(endpoint); - } else if (JBossSAMLConstants.SINGLE_SIGNON_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.SINGLE_SIGNON_SERVICE.get()); - - idpSSODescriptor.addSingleSignOnService(endpoint); - } else if (JBossSAMLConstants.MANAGE_NAMEID_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.MANAGE_NAMEID_SERVICE.get()); - - idpSSODescriptor.addManageNameIDService(endpoint); - } else if (JBossSAMLConstants.NAMEID_MAPPING_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.NAMEID_MAPPING_SERVICE.get()); - - idpSSODescriptor.addNameIDMappingService(endpoint); - } else if (JBossSAMLConstants.NAMEID_FORMAT.get().equalsIgnoreCase(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - idpSSODescriptor.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.ATTRIBUTE.get().equalsIgnoreCase(localPart)) { - AttributeType attribute = SAMLAttributeParser.getInstance().parse(xmlEventReader); - idpSSODescriptor.addAttribute(attribute); - } else if (JBossSAMLConstants.KEY_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { - idpSSODescriptor.addKeyDescriptor(parseKeyDescriptor(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { - idpSSODescriptor.setExtensions(parseExtensions(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - return idpSSODescriptor; - } - - private EndpointType getEndpointType(StartElement startElement) { - Attribute bindingAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.BINDING.get())); - String binding = StaxParserUtil.getAttributeValue(bindingAttr); - - Attribute locationAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.LOCATION.get())); - String location = StaxParserUtil.getAttributeValue(locationAttr); - - EndpointType endpoint = new IndexedEndpointType(URI.create(binding), URI.create(location)); - Attribute responseLocation = startElement.getAttributeByName(new QName(JBossSAMLConstants.RESPONSE_LOCATION.get())); - if (responseLocation != null) { - endpoint.setResponseLocation(URI.create(StaxParserUtil.getAttributeValue(responseLocation))); - } - return endpoint; - } - - private AttributeAuthorityDescriptorType parseAttributeAuthorityDescriptor(XMLEventReader xmlEventReader) - throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ATTRIBUTE_AUTHORITY_DESCRIPTOR.get()); - List protocolEnum = parseProtocolEnumeration(startElement); - AttributeAuthorityDescriptorType attributeAuthority = new AttributeAuthorityDescriptorType(protocolEnum); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.ATTRIBUTE_AUTHORITY_DESCRIPTOR.get()); + EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(spSSO); + EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); + target.addChoiceType(edtChoice); + } break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); + case ATTRIBUTE_AUTHORITY_DESCRIPTOR: + { + AttributeAuthorityDescriptorType attrAuthority = SAMLAttributeAuthorityDescriptorParser.getInstance().parse(xmlEventReader); - if (JBossSAMLConstants.ATTRIBUTE_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - Attribute bindingAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.BINDING.get())); - String binding = StaxParserUtil.getAttributeValue(bindingAttr); - - Attribute locationAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.LOCATION.get())); - String location = StaxParserUtil.getAttributeValue(locationAttr); - - IndexedEndpointType endpoint = new IndexedEndpointType(URI.create(binding), URI.create(location)); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.ATTRIBUTE_SERVICE.get()); - - attributeAuthority.addAttributeService(endpoint); - } else if (JBossSAMLConstants.ASSERTION_ID_REQUEST_SERVICE.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - EndpointType endpoint = getEndpointType(startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.ASSERTION_ID_REQUEST_SERVICE.get()); - - attributeAuthority.addAssertionIDRequestService(endpoint); - } else if (JBossSAMLConstants.ATTRIBUTE_PROFILE.get().equalsIgnoreCase(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - attributeAuthority.addAttributeProfile(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.ATTRIBUTE.get().equalsIgnoreCase(localPart)) { - attributeAuthority.addAttribute(SAMLAttributeParser.getInstance().parse(xmlEventReader)); - } else if (JBossSAMLConstants.KEY_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { - attributeAuthority.addKeyDescriptor(parseKeyDescriptor(xmlEventReader)); - } else if (JBossSAMLConstants.NAMEID_FORMAT.get().equalsIgnoreCase(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - attributeAuthority.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { - attributeAuthority.setExtensions(parseExtensions(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - - } - return attributeAuthority; - } - - private OrganizationType parseOrganization(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ORGANIZATION.get()); - - OrganizationType org = new OrganizationType(); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.ORGANIZATION.get()); + EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(attrAuthority); + EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); + target.addChoiceType(edtChoice); + } break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); + case AUTHN_AUTHORITY_DESCRIPTOR: + { + AuthnAuthorityDescriptorType authAuthority = SAMLAuthnAuthorityDescriptorParser.getInstance().parse(xmlEventReader); - if (JBossSAMLConstants.ORGANIZATION_NAME.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - LocalizedNameType localName = getLocalizedName(xmlEventReader, startElement); - org.addOrganizationName(localName); - } else if (JBossSAMLConstants.ORGANIZATION_DISPLAY_NAME.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - LocalizedNameType localName = getLocalizedName(xmlEventReader, startElement); - org.addOrganizationDisplayName(localName); - } else if (JBossSAMLConstants.ORGANIZATION_URL.get().equals(localPart) || - (JBossSAMLConstants.ORGANIZATION_URL_ALT.get().equals(localPart))) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - Attribute lang = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XML.get(), "lang")); - String langVal = StaxParserUtil.getAttributeValue(lang); - LocalizedURIType localName = new LocalizedURIType(langVal); - localName.setValue(URI.create(StaxParserUtil.getElementText(xmlEventReader))); - org.addOrganizationURL(localName); - } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { - org.setExtensions(parseExtensions(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - return org; - } - - private KeyDescriptorType parseKeyDescriptor(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.KEY_DESCRIPTOR.get()); - - KeyDescriptorType keyDescriptor = new KeyDescriptorType(); - - String use = StaxParserUtil.getAttributeValue(startElement, "use"); - - if (use != null && !use.isEmpty()) { - keyDescriptor.setUse(KeyTypes.fromValue(use)); - } - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.KEY_DESCRIPTOR.get()); + EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(authAuthority); + EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); + target.addChoiceType(edtChoice); + } break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); + case PDP_DESCRIPTOR: + { + PDPDescriptorType pdpDescriptor = SAMLPDPDescriptorParser.getInstance().parse(xmlEventReader); - if (JBossSAMLConstants.KEY_INFO.get().equals(localPart)) { - Element key = StaxParserUtil.getDOMElement(xmlEventReader); - keyDescriptor.setKeyInfo(key); - } else if (JBossSAMLConstants.ENCRYPTION_METHOD.get().equals(localPart)) { - keyDescriptor.addEncryptionMethod(parseEncryptionMethod(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - - return keyDescriptor; - } - - private EncryptionMethodType parseEncryptionMethod(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ENCRYPTION_METHOD.get()); - Attribute algorithm = startElement.getAttributeByName(new QName("Algorithm")); - EncryptionMethodType encryptionMethodType = new EncryptionMethodType(algorithm.getValue()); - - BigInteger keySize = null; - byte[] OAEPparams = null; - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.ENCRYPTION_METHOD.get()); + EntityDescriptorType.EDTDescriptorChoiceType edtDescChoice = new EntityDescriptorType.EDTDescriptorChoiceType(pdpDescriptor); + EntityDescriptorType.EDTChoiceType edtChoice = EntityDescriptorType.EDTChoiceType.oneValue(edtDescChoice); + target.addChoiceType(edtChoice); + } break; - } - - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); - - if ("KeySize".equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - keySize = BigInteger.valueOf(Long.valueOf(StaxParserUtil.getElementText(xmlEventReader))); - } else if ("OAEPparams".equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - OAEPparams = StaxParserUtil.getElementText(xmlEventReader).getBytes(GeneralConstants.SAML_CHARSET); - } else { - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - } - - EncryptionMethodType.EncryptionMethod encryptionMethod = new EncryptionMethodType.EncryptionMethod(keySize, OAEPparams); - - encryptionMethodType.setEncryptionMethod(encryptionMethod); - - return encryptionMethodType; - } - - private ContactType parseContactPerson(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.CONTACT_PERSON.get()); - - Attribute attr = startElement.getAttributeByName(new QName(JBossSAMLConstants.CONTACT_TYPE.get())); - if (attr == null) - throw logger.parserRequiredAttribute("contactType"); - ContactType contactType = new ContactType(ContactTypeType.fromValue(StaxParserUtil.getAttributeValue(attr))); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.CONTACT_PERSON.get()); + case ROLE_DESCRIPTOR: + case AFFILIATION_DESCRIPTOR: + case ADDITIONAL_METADATA_LOCATION: + StaxParserUtil.bypassElementBlock(xmlEventReader); break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); - - if (JBossSAMLConstants.COMPANY.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - contactType.setCompany(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.GIVEN_NAME.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - contactType.setGivenName(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.SURNAME.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - contactType.setSurName(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.EMAIL_ADDRESS.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - contactType.addEmailAddress(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.TELEPHONE_NUMBER.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - contactType.addTelephone(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { - contactType.setExtensions(parseExtensions(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - return contactType; - } - - private LocalizedNameType getLocalizedName(XMLEventReader xmlEventReader, StartElement startElement) - throws ParsingException { - Attribute lang = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XML.get(), "lang")); - String langVal = StaxParserUtil.getAttributeValue(lang); - LocalizedNameType localName = new LocalizedNameType(langVal); - localName.setValue(StaxParserUtil.getElementText(xmlEventReader)); - return localName; - } - - private IndexedEndpointType parseAssertionConsumerService(XMLEventReader xmlEventReader, StartElement startElement) - throws ParsingException { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - IndexedEndpointType endpoint = parseIndexedEndpoint(xmlEventReader, startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE.get()); - - return endpoint; - } - - private IndexedEndpointType parseArtifactResolutionService(XMLEventReader xmlEventReader, StartElement startElement) - throws ParsingException { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - IndexedEndpointType endpoint = parseIndexedEndpoint(xmlEventReader, startElement); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.ARTIFACT_RESOLUTION_SERVICE.get()); - - return endpoint; - } - - private IndexedEndpointType parseIndexedEndpoint(XMLEventReader xmlEventReader, StartElement startElement) { - Attribute bindingAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.BINDING.get())); - String binding = StaxParserUtil.getAttributeValue(bindingAttr); - - Attribute locationAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.LOCATION.get())); - String location = StaxParserUtil.getAttributeValue(locationAttr); - - IndexedEndpointType endpoint = new IndexedEndpointType(URI.create(binding), URI.create(location)); - Attribute isDefault = startElement.getAttributeByName(new QName(JBossSAMLConstants.ISDEFAULT.get())); - if (isDefault != null) { - endpoint.setIsDefault(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(isDefault))); - } - Attribute index = startElement.getAttributeByName(new QName(JBossSAMLConstants.INDEX.get())); - if (index != null) { - endpoint.setIndex(Integer.parseInt(StaxParserUtil.getAttributeValue(index))); - } - return endpoint; - } - - private AttributeConsumingServiceType parseAttributeConsumingService(XMLEventReader xmlEventReader, - StartElement startElement) throws ParsingException { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - - Attribute indexAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.INDEX.get())); - if (indexAttr == null) - throw logger.parserRequiredAttribute("index"); - - AttributeConsumingServiceType attributeConsumer = new AttributeConsumingServiceType(Integer.parseInt(StaxParserUtil - .getAttributeValue(indexAttr))); - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.ATTRIBUTE_CONSUMING_SERVICE.get()); + case ORGANIZATION: + target.setOrganization(SAMLOrganizationParser.getInstance().parse(xmlEventReader)); break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); - - if (JBossSAMLConstants.SERVICE_NAME.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - LocalizedNameType localName = getLocalizedName(xmlEventReader, startElement); - attributeConsumer.addServiceName(localName); - } else if (JBossSAMLConstants.SERVICE_DESCRIPTION.get().equals(localPart)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - LocalizedNameType localName = getLocalizedName(xmlEventReader, startElement); - attributeConsumer.addServiceDescription(localName); - } else if (JBossSAMLConstants.REQUESTED_ATTRIBUTE.get().equals(localPart)) { - attributeConsumer.addRequestedAttribute(SAMLRequestedAttributeParser.getInstance().parse(xmlEventReader)); - } else - throw logger.parserUnknownTag(localPart, startElement.getLocation()); - } - - return attributeConsumer; - } - - private ExtensionsType parseExtensions(XMLEventReader xmlEventReader) throws ParsingException { - ExtensionsType extensions = new ExtensionsType(); - Element extElement = StaxParserUtil.getDOMElement(xmlEventReader); - extensions.setElement(extElement); - return extensions; - } - - private RoleDescriptorType parseRoleDescriptor(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ROLE_DESCRIPTOR.get()); - List protocolEnum = parseProtocolEnumeration(startElement); - RoleDescriptorType roleDescriptorType = new RoleDescriptorType(protocolEnum) {}; - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(end, JBossSAMLConstants.ROLE_DESCRIPTOR.get()); + case CONTACT_PERSON: + target.addContactPerson(SAMLContactPersonParser.getInstance().parse(xmlEventReader)); break; - } - startElement = (StartElement) xmlEvent; - String localPart = startElement.getName().getLocalPart(); - - if (JBossSAMLConstants.KEY_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { - KeyDescriptorType keyDescriptor = parseKeyDescriptor(xmlEventReader); - roleDescriptorType.addKeyDescriptor(keyDescriptor); - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, localPart); - } - } - - return roleDescriptorType; - } - - /** - * Parse a space delimited list of strings - * - * @param startElement - * - * @return - */ - public static List parseProtocolEnumeration(StartElement startElement) { - List protocolEnum = new ArrayList<>(); - Attribute proto = startElement.getAttributeByName(JBossSAMLConstants.PROTOCOL_SUPPORT_ENUMERATION.getAsQName()); - String val = StaxParserUtil.getAttributeValue(proto); - if (StringUtil.isNotNull(val)) { - StringTokenizer st = new StringTokenizer(val); - while (st.hasMoreTokens()) { - protocolEnum.add(st.nextToken()); - } + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); } - return protocolEnum; } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLExtensionsParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLExtensionsParser.java index 585c88f1f2..4dc8ab53fd 100644 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLExtensionsParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLExtensionsParser.java @@ -16,24 +16,24 @@ */ package org.keycloak.saml.processing.core.parsers.saml.metadata; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.StartElement; import org.keycloak.dom.saml.v2.metadata.ExtensionsType; -import org.keycloak.saml.common.constants.JBossSAMLConstants; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.util.StaxParserUtil; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + /** * Parses <samlp:Extensions> SAML2 element into series of DOM nodes. * * @author hmlnarik */ -public class SAMLExtensionsParser extends AbstractStaxSamlParser { +public class SAMLExtensionsParser extends AbstractStaxSamlMetadataParser { private static final SAMLExtensionsParser INSTANCE = new SAMLExtensionsParser(); private SAMLExtensionsParser() { - super(JBossSAMLConstants.EXTENSIONS__METADATA); + super(SAMLMetadataQNames.EXTENSIONS); } public static SAMLExtensionsParser getInstance() { @@ -46,7 +46,7 @@ public class SAMLExtensionsParser extends AbstractStaxSamlParser } @Override - protected void processSubElement(XMLEventReader xmlEventReader, ExtensionsType target, JBossSAMLConstants element, StartElement elementDetail) throws ParsingException { - target.setElement(StaxParserUtil.getDOMElement(xmlEventReader)); + protected void processSubElement(XMLEventReader xmlEventReader, ExtensionsType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + target.addExtension(StaxParserUtil.getDOMElement(xmlEventReader)); } } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLIDPSSODescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLIDPSSODescriptorParser.java new file mode 100644 index 0000000000..f90b7b6129 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLIDPSSODescriptorParser.java @@ -0,0 +1,73 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.util.List; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.IDP_SSO_DESCRIPTOR; + +/** + * @author mhajas + */ +public class SAMLIDPSSODescriptorParser extends SAMLSSODescriptorTypeParser { + + private static final SAMLIDPSSODescriptorParser INSTANCE = new SAMLIDPSSODescriptorParser(); + + private SAMLIDPSSODescriptorParser() { + super(IDP_SSO_DESCRIPTOR); + } + + public static SAMLIDPSSODescriptorParser getInstance() { + return INSTANCE; + } + + @Override + protected IDPSSODescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + List protocolEnum = StaxParserUtil.getRequiredStringListAttributeValue(element, SAMLMetadataQNames.ATTR_PROTOCOL_SUPPORT_ENUMERATION); + IDPSSODescriptorType descriptor = new IDPSSODescriptorType(protocolEnum); + + // Role descriptor optional arguments + parseOptionalArguments(element, descriptor); + + // IDPSSODecsriptor optional attributes + Boolean wantAuthnRequestsSigned = StaxParserUtil.getBooleanAttributeValue(element, SAMLMetadataQNames.ATTR_WANT_AUTHN_REQUESTS_SIGNED); + if (wantAuthnRequestsSigned != null) { + descriptor.setWantAuthnRequestsSigned(wantAuthnRequestsSigned); + } + + return descriptor; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, IDPSSODescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case SINGLE_SIGNON_SERVICE: + target.addSingleSignOnService(SAMLSingleSignOnServiceParser.getInstance().parse(xmlEventReader)); + break; + + case NAMEID_MAPPING_SERVICE: + target.addNameIDMappingService(SAMLNameIDMappingServiceParser.getInstance().parse(xmlEventReader)); + break; + + case ASSERTION_ID_REQUEST_SERVICE: + target.addAssertionIDRequestService(SAMLAssertinIDRequestServiceParser.getInstance().parse(xmlEventReader)); + break; + + case ATTRIBUTE_PROFILE: + StaxParserUtil.advance(xmlEventReader); + target.addAttributeProfile(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case ATTRIBUTE: + target.addAttribute(SAMLAttributeParser.getInstance().parse(xmlEventReader)); + break; + + default: + super.processSubElement(xmlEventReader, target, element, elementDetail); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLIndexedEndpointTypeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLIndexedEndpointTypeParser.java new file mode 100644 index 0000000000..68b5e170fe --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLIndexedEndpointTypeParser.java @@ -0,0 +1,50 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.net.URI; + +/** + * @author mhajas + */ +public abstract class SAMLIndexedEndpointTypeParser extends AbstractStaxSamlMetadataParser { + + public SAMLIndexedEndpointTypeParser(SAMLMetadataQNames expectedStartElement) { + super(expectedStartElement); + } + + @Override + protected IndexedEndpointType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + String binding = StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_BINDING); + String location = StaxParserUtil.getRequiredAttributeValue(element, SAMLMetadataQNames.ATTR_LOCATION); + + IndexedEndpointType endpoint = new IndexedEndpointType(URI.create(binding), URI.create(location)); + + Boolean isDefault = StaxParserUtil.getBooleanAttributeValue(element, SAMLMetadataQNames.ATTR_IS_DEFAULT); + if (isDefault != null) { + endpoint.setIsDefault(isDefault); + } + + Integer index = StaxParserUtil.getIntegerAttributeValue(element, SAMLMetadataQNames.ATTR_INDEX); + if (index != null) + endpoint.setIndex(index); + + // EndpointType attributes + String responseLocation = StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_RESPONSE_LOCATION); + + if (responseLocation != null) { + endpoint.setResponseLocation(URI.create(responseLocation)); + } + + return endpoint; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, IndexedEndpointType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLKeyDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLKeyDescriptorParser.java new file mode 100644 index 0000000000..3e435ff525 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLKeyDescriptorParser.java @@ -0,0 +1,56 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.KEY_DESCRIPTOR; + +/** + * @author mhajas + */ +public class SAMLKeyDescriptorParser extends AbstractStaxSamlMetadataParser { + + private static final SAMLKeyDescriptorParser INSTANCE = new SAMLKeyDescriptorParser(); + + public SAMLKeyDescriptorParser() { + super(KEY_DESCRIPTOR); + } + + public static SAMLKeyDescriptorParser getInstance() { + return INSTANCE; + } + + @Override + protected KeyDescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + KeyDescriptorType keyDescriptor = new KeyDescriptorType(); + + String use = StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_USE); + + if (use != null && !use.isEmpty()) { + keyDescriptor.setUse(KeyTypes.fromValue(use)); + } + + return keyDescriptor; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, KeyDescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch(element) { + case KEY_INFO: + target.setKeyInfo(StaxParserUtil.getDOMElement(xmlEventReader)); + break; + + case ENCRYPTION_METHOD: + target.addEncryptionMethod(SAMLEncryptionMethodParser.getInstance().parse(xmlEventReader)); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLManageNameIDServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLManageNameIDServiceParser.java new file mode 100644 index 0000000000..715f28c752 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLManageNameIDServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLManageNameIDServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLManageNameIDServiceParser INSTANCE = new SAMLManageNameIDServiceParser(); + + public SAMLManageNameIDServiceParser() { + super(SAMLMetadataQNames.MANAGE_NAMEID_SERVICE); + } + + public static SAMLManageNameIDServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLMetadataQNames.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLMetadataQNames.java new file mode 100644 index 0000000000..250748a5df --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLMetadataQNames.java @@ -0,0 +1,113 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionQNames; +import org.keycloak.saml.processing.core.parsers.saml.xmldsig.XmlDSigQNames; +import org.keycloak.saml.processing.core.parsers.util.HasQName; + +import javax.xml.namespace.QName; + + +/** + * @author mhajas + */ +public enum SAMLMetadataQNames implements HasQName { + ADDITIONAL_METADATA_LOCATION("AdditionalMetadataLocation"), + AFFILIATE_MEMBER("AffiliateMember"), + AFFILIATION_DESCRIPTOR("AffiliationDescriptor"), + ARTIFACT_RESOLUTION_SERVICE("ArtifactResolutionService"), + ASSERTION_CONSUMER_SERVICE("AssertionConsumerService"), + ASSERTION_ID_REQUEST_SERVICE("AssertionIDRequestService"), + ATTRIBUTE_AUTHORITY_DESCRIPTOR("AttributeAuthorityDescriptor"), + ATTRIBUTE_CONSUMING_SERVICE("AttributeConsumingService"), + ATTRIBUTE_PROFILE("AttributeProfile"), + ATTRIBUTE_SERVICE("AttributeService"), + ATTRIBUTE_VALUE("AttributeValue"), + AUTHN_AUTHORITY_DESCRIPTOR("AuthnAuthorityDescriptor"), + AUTHN_QUERY_SERVICE("AuthnQueryService"), + AUTHZ_SERVICE("AuthzService"), + COMPANY("Company"), + CONTACT_PERSON("ContactPerson"), + EMAIL_ADDRESS("EmailAddress"), + ENCRYPTION_METHOD("EncryptionMethod"), + ENTITIES_DESCRIPTOR("EntitiesDescriptor"), + ENTITY_DESCRIPTOR("EntityDescriptor"), + EXTENSIONS("Extensions"), + GIVEN_NAME("GivenName"), + IDP_SSO_DESCRIPTOR("IDPSSODescriptor"), + KEY_DESCRIPTOR("KeyDescriptor"), + MANAGE_NAMEID_SERVICE("ManageNameIDService"), + NAMEID_FORMAT("NameIDFormat"), + NAMEID_MAPPING_SERVICE("NameIDMappingService"), + ORGANIZATION_DISPLAY_NAME("OrganizationDisplayName"), + ORGANIZATION_NAME("OrganizationName"), + ORGANIZATION("Organization"), + ORGANIZATION_URL("OrganizationURL"), + ORGANIZATION_URL_ALT("OrganizationUrl"), // non-standard: KEYCLOAK-4040, + PDP_DESCRIPTOR("PDPDescriptor"), + REQUESTED_ATTRIBUTE("RequestedAttribute"), + ROLE_DESCRIPTOR("RoleDescriptor"), + SERVICE_DESCRIPTION("ServiceDescription"), + SERVICE_NAME("ServiceName"), + SINGLE_LOGOUT_SERVICE("SingleLogoutService"), + SINGLE_SIGNON_SERVICE("SingleSignOnService"), + SP_SSO_DESCRIPTOR("SPSSODescriptor"), + SURNAME("SurName"), + TELEPHONE_NUMBER("TelephoneNumber"), + + // Attribute names + ATTR_ENTITY_ID(null, "entityID"), + ATTR_ID(null, "ID"), + ATTR_VALID_UNTIL(null, "validUntil"), + ATTR_CACHE_DURATION(null, "cacheDuration"), + ATTR_PROTOCOL_SUPPORT_ENUMERATION(null, "protocolSupportEnumeration"), + ATTR_USE(null, "use"), + ATTR_ALGORITHM(null, "Algorithm"), + ATTR_LANG(JBossSAMLURIConstants.XML, "lang"), + ATTR_CONTACT_TYPE(null, "contactType"), + ATTR_AUTHN_REQUESTS_SIGNED(null, "AuthnRequestsSigned"), + ATTR_WANT_ASSERTIONS_SIGNED(null, "WantAssertionsSigned"), + ATTR_WANT_AUTHN_REQUESTS_SIGNED(null, "WantAuthnRequestsSigned"), + ATTR_BINDING(null, "Binding"), + ATTR_LOCATION(null, "Location"), + ATTR_IS_DEFAULT(null, "isDefault"), + ATTR_INDEX(null, "index"), + ATTR_RESPONSE_LOCATION(null, "ResponseLocation"), + ATTR_FRIENDLY_NAME(null, "FriendlyName"), + ATTR_IS_REQUIRED(null, "isRequired"), + ATTR_NAME(null, "Name"), + ATTR_NAME_FORMAT(null, "NameFormat"), + + // Elements from other namespaces that can be direct subelements of this namespace's elements + SIGNATURE(XmlDSigQNames.SIGNATURE), + KEY_INFO(XmlDSigQNames.KEY_INFO), + KEY_SIZE(JBossSAMLURIConstants.XMLENC_NSURI, "KeySize"), + OAEP_PARAMS(JBossSAMLURIConstants.XMLENC_NSURI, "OAEPparams"), + ATTR_X500_ENCODING(JBossSAMLURIConstants.X500_NSURI, "Encoding"), + ATTRIBUTE(SAMLAssertionQNames.ATTRIBUTE), + + UNKNOWN_ELEMENT(""); + + private final QName qName; + + private SAMLMetadataQNames(String localName) { + this.qName = new QName(JBossSAMLURIConstants.METADATA_NSURI.get(), localName); + } + + private SAMLMetadataQNames(HasQName source) { + this.qName = source.getQName(); + } + + private SAMLMetadataQNames(JBossSAMLURIConstants nsUri, String localName) { + this.qName = new QName(nsUri == null ? null : nsUri.get(), localName); + } + + @Override + public QName getQName() { + return qName; + } + + public QName getQName(String prefix) { + return new QName(this.qName.getNamespaceURI(), this.qName.getLocalPart(), prefix); + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLNameIDMappingServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLNameIDMappingServiceParser.java new file mode 100644 index 0000000000..c978fbef60 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLNameIDMappingServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLNameIDMappingServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLNameIDMappingServiceParser INSTANCE = new SAMLNameIDMappingServiceParser(); + + public SAMLNameIDMappingServiceParser() { + super(SAMLMetadataQNames.NAMEID_MAPPING_SERVICE); + } + + public static SAMLNameIDMappingServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLOrganizationParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLOrganizationParser.java new file mode 100644 index 0000000000..da3e2277bb --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLOrganizationParser.java @@ -0,0 +1,68 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; +import org.keycloak.dom.saml.v2.metadata.LocalizedURIType; +import org.keycloak.dom.saml.v2.metadata.OrganizationType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.net.URI; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.ATTR_LANG; +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.ORGANIZATION; + +/** + * @author mhajas + */ +public class SAMLOrganizationParser extends AbstractStaxSamlMetadataParser { + + private static final SAMLOrganizationParser INSTANCE = new SAMLOrganizationParser(); + + public SAMLOrganizationParser() { + super(ORGANIZATION); + } + + public static SAMLOrganizationParser getInstance() { + return INSTANCE; + } + @Override + protected OrganizationType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new OrganizationType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, OrganizationType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ORGANIZATION_NAME: + LocalizedNameType orgName = new LocalizedNameType(StaxParserUtil.getAttributeValue(elementDetail, ATTR_LANG)); + StaxParserUtil.advance(xmlEventReader); + orgName.setValue(StaxParserUtil.getElementText(xmlEventReader)); + target.addOrganizationName(orgName); + break; + + case ORGANIZATION_DISPLAY_NAME: + LocalizedNameType orgDispName = new LocalizedNameType(StaxParserUtil.getAttributeValue(elementDetail, ATTR_LANG)); + StaxParserUtil.advance(xmlEventReader); + orgDispName.setValue(StaxParserUtil.getElementText(xmlEventReader)); + target.addOrganizationDisplayName(orgDispName); + break; + + case ORGANIZATION_URL: + case ORGANIZATION_URL_ALT: + LocalizedURIType orgURL = new LocalizedURIType(StaxParserUtil.getAttributeValue(elementDetail, ATTR_LANG)); + StaxParserUtil.advance(xmlEventReader); + orgURL.setValue(URI.create(StaxParserUtil.getElementText(xmlEventReader))); + target.addOrganizationURL(orgURL); + break; + + case EXTENSIONS: + target.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLPDPDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLPDPDescriptorParser.java new file mode 100644 index 0000000000..07239e6bb8 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLPDPDescriptorParser.java @@ -0,0 +1,56 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.PDPDescriptorType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.util.List; + +/** + * @author mhajas + */ +public class SAMLPDPDescriptorParser extends SAMLRoleDecriptorTypeParser { + + private static final SAMLPDPDescriptorParser INSTANCE = new SAMLPDPDescriptorParser(); + + public SAMLPDPDescriptorParser() { + super(SAMLMetadataQNames.PDP_DESCRIPTOR); + } + + public static SAMLPDPDescriptorParser getInstance() { + return INSTANCE; + } + + @Override + protected PDPDescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + List protocolEnum = StaxParserUtil.getRequiredStringListAttributeValue(element, SAMLMetadataQNames.ATTR_PROTOCOL_SUPPORT_ENUMERATION); + PDPDescriptorType descriptor = new PDPDescriptorType(protocolEnum); + + parseOptionalArguments(element, descriptor); + + return descriptor; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, PDPDescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case AUTHZ_SERVICE: + target.addAuthZService(SAMLAuthzServiceParser.getInstance().parse(xmlEventReader)); + break; + + case ASSERTION_ID_REQUEST_SERVICE: + target.addAssertionIDRequestService(SAMLAssertinIDRequestServiceParser.getInstance().parse(xmlEventReader)); + break; + + case NAMEID_FORMAT: + StaxParserUtil.advance(xmlEventReader); + target.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); + break; + + default: + super.processSubElement(xmlEventReader, target, element, elementDetail); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLRequestedAttributeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLRequestedAttributeParser.java index b1dbb7d454..77543ebbd5 100644 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLRequestedAttributeParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLRequestedAttributeParser.java @@ -17,31 +17,25 @@ package org.keycloak.saml.processing.core.parsers.saml.metadata; import org.keycloak.dom.saml.v2.metadata.RequestedAttributeType; -import org.keycloak.saml.common.constants.JBossSAMLConstants; -import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.util.StaxParserUtil; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; -import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeValueParser; import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionQNames; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeValueParser; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; /** * Parse the in the saml assertion * * @since Oct 14, 2010 */ -public class SAMLRequestedAttributeParser extends AbstractStaxSamlParser { +public class SAMLRequestedAttributeParser extends AbstractStaxSamlMetadataParser { private static final SAMLRequestedAttributeParser INSTANCE = new SAMLRequestedAttributeParser(); - private static final QName X500_ENCODING = new QName(JBossSAMLURIConstants.X500_NSURI.get(), JBossSAMLConstants.ENCODING.get(), - JBossSAMLURIConstants.X500_PREFIX.get()); - private SAMLRequestedAttributeParser() { - super(JBossSAMLConstants.REQUESTED_ATTRIBUTE); + super(SAMLMetadataQNames.REQUESTED_ATTRIBUTE); } public static SAMLRequestedAttributeParser getInstance() { @@ -50,24 +44,22 @@ public class SAMLRequestedAttributeParser extends AbstractStaxSamlParser extends AbstractStaxSamlMetadataParser { + + public SAMLRoleDecriptorTypeParser(SAMLMetadataQNames expectedStartElement) { + super(expectedStartElement); + } + + protected void parseOptionalArguments(StartElement element, RoleDescriptorType descriptor) throws ParsingException { + descriptor.setID(StaxParserUtil.getAttributeValue(element, SAMLMetadataQNames.ATTR_ID)); + descriptor.setValidUntil(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLMetadataQNames.ATTR_VALID_UNTIL)); + descriptor.setCacheDuration(StaxParserUtil.getXmlDurationAttributeValue(element, SAMLMetadataQNames.ATTR_CACHE_DURATION)); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, T target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case KEY_DESCRIPTOR: + target.addKeyDescriptor(SAMLKeyDescriptorParser.getInstance().parse(xmlEventReader)); + break; + + case SIGNATURE: + Element sig = StaxParserUtil.getDOMElement(xmlEventReader); + target.setSignature(sig); + break; + + case EXTENSIONS: + target.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); + break; + + case ORGANIZATION: + target.setOrganization(SAMLOrganizationParser.getInstance().parse(xmlEventReader)); + break; + + case CONTACT_PERSON: + target.addContactPerson(SAMLContactPersonParser.getInstance().parse(xmlEventReader)); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSPSSODescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSPSSODescriptorParser.java new file mode 100644 index 0000000000..84eeb1a99c --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSPSSODescriptorParser.java @@ -0,0 +1,65 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.util.List; + +import static org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLMetadataQNames.SP_SSO_DESCRIPTOR; + +/** + * @author mhajas + */ +public class SAMLSPSSODescriptorParser extends SAMLSSODescriptorTypeParser { + + private static final SAMLSPSSODescriptorParser INSTANCE = new SAMLSPSSODescriptorParser(); + + private SAMLSPSSODescriptorParser() { + super(SP_SSO_DESCRIPTOR); + } + + public static SAMLSPSSODescriptorParser getInstance() { + return INSTANCE; + } + + @Override + protected SPSSODescriptorType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + List protocolEnum = StaxParserUtil.getRequiredStringListAttributeValue(element, SAMLMetadataQNames.ATTR_PROTOCOL_SUPPORT_ENUMERATION); + SPSSODescriptorType descriptor = new SPSSODescriptorType(protocolEnum); + + // Role descriptor optional arguments + parseOptionalArguments(element, descriptor); + + // SPSSODecsriptor optional attributes + Boolean authnRequestsSigned = StaxParserUtil.getBooleanAttributeValue(element, SAMLMetadataQNames.ATTR_AUTHN_REQUESTS_SIGNED); + if (authnRequestsSigned != null) { + descriptor.setAuthnRequestsSigned(authnRequestsSigned); + } + + Boolean wantAssertionSigned = StaxParserUtil.getBooleanAttributeValue(element, SAMLMetadataQNames.ATTR_WANT_ASSERTIONS_SIGNED); + if (wantAssertionSigned != null) { + descriptor.setWantAssertionsSigned(wantAssertionSigned); + } + + return descriptor; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, SPSSODescriptorType target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ASSERTION_CONSUMER_SERVICE: + target.addAssertionConsumerService(SAMLAssertionConsumerServiceParser.getInstance().parse(xmlEventReader)); + break; + + case ATTRIBUTE_CONSUMING_SERVICE: + target.addAttributeConsumerService(SAMLAttributeConsumingServiceParser.getInstance().parse(xmlEventReader)); + break; + + default: + super.processSubElement(xmlEventReader, target, element, elementDetail); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSSODescriptorTypeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSSODescriptorTypeParser.java new file mode 100644 index 0000000000..65ca09736a --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSSODescriptorTypeParser.java @@ -0,0 +1,43 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +import org.keycloak.dom.saml.v2.metadata.SSODescriptorType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author mhajas + */ +public abstract class SAMLSSODescriptorTypeParser extends SAMLRoleDecriptorTypeParser { + + public SAMLSSODescriptorTypeParser(SAMLMetadataQNames expectedStartElement) { + super(expectedStartElement); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, T target, SAMLMetadataQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ARTIFACT_RESOLUTION_SERVICE: + target.addArtifactResolutionService(SAMLArtifactResolutionServiceParser.getInstance().parse(xmlEventReader)); + break; + + case SINGLE_LOGOUT_SERVICE: + target.addSingleLogoutService(SAMLSingleLogoutServiceParser.getInstance().parse(xmlEventReader)); + break; + + case MANAGE_NAMEID_SERVICE: + target.addSingleLogoutService(SAMLManageNameIDServiceParser.getInstance().parse(xmlEventReader)); + break; + + case NAMEID_FORMAT: + StaxParserUtil.advance(xmlEventReader); + target.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); + break; + + default: + super.processSubElement(xmlEventReader, target, element, elementDetail); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSingleLogoutServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSingleLogoutServiceParser.java new file mode 100644 index 0000000000..7d7abd6ee1 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSingleLogoutServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLSingleLogoutServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLSingleLogoutServiceParser INSTANCE = new SAMLSingleLogoutServiceParser(); + + public SAMLSingleLogoutServiceParser() { + super(SAMLMetadataQNames.SINGLE_LOGOUT_SERVICE); + } + + public static SAMLSingleLogoutServiceParser getInstance() { + return INSTANCE; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSingleSignOnServiceParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSingleSignOnServiceParser.java new file mode 100644 index 0000000000..b713ae3b16 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSingleSignOnServiceParser.java @@ -0,0 +1,17 @@ +package org.keycloak.saml.processing.core.parsers.saml.metadata; + +/** + * @author mhajas + */ +public class SAMLSingleSignOnServiceParser extends SAMLEndpointTypeParser { + + private static final SAMLSingleSignOnServiceParser INSTANCE = new SAMLSingleSignOnServiceParser(); + + public SAMLSingleSignOnServiceParser() { + super(SAMLMetadataQNames.SINGLE_SIGNON_SERVICE); + } + + public static SAMLSingleSignOnServiceParser getInstance() { + return INSTANCE; + } +} 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 9ab24be51b..c395f86850 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,24 +16,12 @@ */ package org.keycloak.saml.processing.core.parsers.saml; -import static org.hamcrest.Matchers.*; -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.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.security.PrivateKey; - +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - import org.keycloak.common.util.Base64; import org.keycloak.common.util.DerUtils; import org.keycloak.common.util.StreamUtil; @@ -50,7 +38,21 @@ import org.keycloak.dom.saml.v2.assertion.NameIDType; import org.keycloak.dom.saml.v2.assertion.StatementAbstractType; import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationType; import org.keycloak.dom.saml.v2.assertion.SubjectType; +import org.keycloak.dom.saml.v2.metadata.AttributeAuthorityDescriptorType; +import org.keycloak.dom.saml.v2.metadata.AttributeConsumingServiceType; +import org.keycloak.dom.saml.v2.metadata.AuthnAuthorityDescriptorType; +import org.keycloak.dom.saml.v2.metadata.EndpointType; +import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType; import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType; +import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType; +import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType; +import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; +import org.keycloak.dom.saml.v2.metadata.LocalizedNameType; +import org.keycloak.dom.saml.v2.metadata.LocalizedURIType; +import org.keycloak.dom.saml.v2.metadata.PDPDescriptorType; +import org.keycloak.dom.saml.v2.metadata.RequestedAttributeType; +import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; import org.keycloak.dom.saml.v2.protocol.ResponseType; @@ -60,6 +62,7 @@ import org.keycloak.dom.xmlsec.w3.xmldsig.KeyInfoType; import org.keycloak.dom.xmlsec.w3.xmldsig.RSAKeyValueType; import org.keycloak.dom.xmlsec.w3.xmldsig.X509CertificateType; import org.keycloak.dom.xmlsec.w3.xmldsig.X509DataType; +import org.keycloak.dom.xmlsec.w3.xmlenc.EncryptionMethodType; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ParsingException; @@ -67,11 +70,38 @@ import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response; import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil; import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; -import java.net.URI; -import java.util.List; -import org.hamcrest.Matcher; import org.w3c.dom.Element; +import javax.xml.namespace.QName; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyCollectionOf; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.everyItem; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.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; + /** * Test class for SAML parser. * @@ -251,12 +281,341 @@ public class SAMLParserTest { @Test public void testSaml20MetadataEntityDescriptorIdP() throws Exception { - assertParsed("saml20-entity-descriptor-idp.xml", EntityDescriptorType.class); + EntityDescriptorType entityDescriptor = assertParsed("saml20-entity-descriptor-idp.xml", EntityDescriptorType.class); + + List descriptors = entityDescriptor.getChoiceType(); + assertThat(descriptors, hasSize(2)); + + // IDPSSO descriptor + IDPSSODescriptorType idpDescriptor = descriptors.get(0).getDescriptors().get(0).getIdpDescriptor(); + assertThat(idpDescriptor, is(notNullValue())); + assertThat(idpDescriptor.isWantAuthnRequestsSigned(), is(true)); + assertThat(idpDescriptor.getProtocolSupportEnumeration(), contains("urn:oasis:names:tc:SAML:2.0:protocol")); + + // Key descriptor + List keyDescriptors = idpDescriptor.getKeyDescriptor(); + assertThat(keyDescriptors, hasSize(1)); + + KeyDescriptorType signingKey = keyDescriptors.get(0); + assertThat(signingKey.getUse(), is(KeyTypes.SIGNING)); + assertThat(signingKey.getEncryptionMethod(), is(emptyCollectionOf(EncryptionMethodType.class))); + assertThat(signingKey.getKeyInfo().getElementsByTagName("ds:KeyName").item(0).getTextContent(), is("IdentityProvider.com SSO Key")); + + // Single logout services + assertThat(idpDescriptor.getSingleLogoutService(), hasSize(2)); + EndpointType singleLS1 = idpDescriptor.getSingleLogoutService().get(0); + assertThat(singleLS1.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:SOAP"))); + assertThat(singleLS1.getLocation(), is(URI.create("https://IdentityProvider.com/SAML/SLO/SOAP"))); + assertThat(singleLS1.getResponseLocation(), is(nullValue())); + assertThat(singleLS1.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(singleLS1.getOtherAttributes(), is(Collections.emptyMap())); + + EndpointType singleLS2 = idpDescriptor.getSingleLogoutService().get(1); + assertThat(singleLS2.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"))); + assertThat(singleLS2.getLocation(), is(URI.create("https://IdentityProvider.com/SAML/SLO/Browser"))); + assertThat(singleLS2.getResponseLocation(), is(URI.create("https://IdentityProvider.com/SAML/SLO/Response"))); + assertThat(singleLS2.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(singleLS2.getOtherAttributes(), is(Collections.emptyMap())); + + // NameID + assertThat(idpDescriptor.getNameIDFormat(), + containsInAnyOrder("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + )); + + // Single sign on services + assertThat(idpDescriptor.getSingleSignOnService(), hasSize(2)); + + EndpointType singleSO1 = idpDescriptor.getSingleSignOnService().get(0); + assertThat(singleSO1.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"))); + assertThat(singleSO1.getLocation(), is(URI.create("https://IdentityProvider.com/SAML/SSO/Browser"))); + assertThat(singleSO1.getResponseLocation(), is(nullValue())); + assertThat(singleSO1.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(singleSO1.getOtherAttributes(), is(Collections.emptyMap())); + + EndpointType singleSO2 = idpDescriptor.getSingleSignOnService().get(1); + assertThat(singleSO2.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"))); + assertThat(singleSO2.getLocation(), is(URI.create("https://IdentityProvider.com/SAML/SSO/Browser"))); + assertThat(singleSO2.getResponseLocation(), is(nullValue())); + assertThat(singleSO2.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(singleSO2.getOtherAttributes(), is(Collections.emptyMap())); + + // Attributes + assertThat(idpDescriptor.getAttribute(), hasSize(2)); + + AttributeType attr1 = idpDescriptor.getAttribute().get(0); + assertThat(attr1.getNameFormat(), is("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")); + assertThat(attr1.getName(), is("urn:oid:1.3.6.1.4.1.5923.1.1.1.6")); + assertThat(attr1.getFriendlyName(), is("eduPersonPrincipalName")); + assertThat(attr1.getOtherAttributes(), is(Collections.emptyMap())); + assertThat(attr1.getAttributeValue(), is(emptyCollectionOf(Object.class))); + + AttributeType attr2 = idpDescriptor.getAttribute().get(1); + assertThat(attr2.getNameFormat(), is("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")); + assertThat(attr2.getName(), is("urn:oid:1.3.6.1.4.1.5923.1.1.1.1")); + assertThat(attr2.getFriendlyName(), is("eduPersonAffiliation")); + assertThat(attr2.getOtherAttributes(), is(Collections.emptyMap())); + assertThat(attr2.getAttributeValue(), containsInAnyOrder((Object) "member", "student", "faculty", "employee", "staff")); + + // Organization + assertThat(entityDescriptor.getOrganization().getOrganizationName(), hasSize(1)); + LocalizedNameType orgName = entityDescriptor.getOrganization().getOrganizationName().get(0); + assertThat(orgName.getLang(), is("en")); + assertThat(orgName.getValue(), is("Identity Providers R\n US")); + + assertThat(entityDescriptor.getOrganization().getOrganizationDisplayName(), hasSize(1)); + LocalizedNameType orgDispName = entityDescriptor.getOrganization().getOrganizationDisplayName().get(0); + assertThat(orgDispName.getLang(), is("en")); + assertThat(orgDispName.getValue(), is("Identity Providers R US, a Division of Lerxst Corp.")); + + assertThat(entityDescriptor.getOrganization().getOrganizationURL(), hasSize(1)); + LocalizedURIType orgURL = entityDescriptor.getOrganization().getOrganizationURL().get(0); + assertThat(orgURL.getLang(), is("en")); + assertThat(orgURL.getValue(), is(URI.create("https://IdentityProvider.com"))); + } + + @Test + public void testSAML20MetadataEntityDescriptorAttrA() throws Exception{ + EntityDescriptorType entityDescriptor = assertParsed("saml20-entity-descriptor-idp.xml", EntityDescriptorType.class); + + List descriptors = entityDescriptor.getChoiceType(); + assertThat(descriptors, hasSize(2)); + + AttributeAuthorityDescriptorType aaDescriptor = descriptors.get(1).getDescriptors().get(0).getAttribDescriptor(); + assertThat(aaDescriptor, is(notNullValue())); + assertThat(aaDescriptor.getProtocolSupportEnumeration(), contains("urn:oasis:names:tc:SAML:2.0:protocol")); + + // Key descriptor + List keyDescriptors = aaDescriptor.getKeyDescriptor(); + assertThat(keyDescriptors, hasSize(1)); + + KeyDescriptorType signingKey = keyDescriptors.get(0); + assertThat(signingKey.getUse(), is(KeyTypes.SIGNING)); + assertThat(signingKey.getEncryptionMethod(), is(emptyCollectionOf(EncryptionMethodType.class))); + assertThat(signingKey.getKeyInfo().getElementsByTagName("ds:KeyName").item(0).getTextContent(), is("IdentityProvider.com AA Key")); + + // Attribute service + assertThat(aaDescriptor.getAttributeService(), hasSize(1)); + EndpointType attrServ = aaDescriptor.getAttributeService().get(0); + assertThat(attrServ.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:SOAP"))); + assertThat(attrServ.getLocation(), is(URI.create("https://IdentityProvider.com/SAML/AA/SOAP"))); + assertThat(attrServ.getResponseLocation(), is(nullValue())); + assertThat(attrServ.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(attrServ.getOtherAttributes(), is(Collections.emptyMap())); + + // AssertionIDRequestService + assertThat(aaDescriptor.getAssertionIDRequestService(), hasSize(1)); + EndpointType assertIDRServ = aaDescriptor.getAssertionIDRequestService().get(0); + assertThat(assertIDRServ.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:URI"))); + assertThat(assertIDRServ.getLocation(), is(URI.create("https://IdentityProvider.com/SAML/AA/URI"))); + assertThat(assertIDRServ.getResponseLocation(), is(nullValue())); + assertThat(assertIDRServ.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(assertIDRServ.getOtherAttributes(), is(Collections.emptyMap())); + + // NameID + assertThat(aaDescriptor.getNameIDFormat(), + containsInAnyOrder("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName", + "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", + "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" + )); + + assertThat(aaDescriptor.getAttribute(), hasSize(2)); + + AttributeType attr1 = aaDescriptor.getAttribute().get(0); + assertThat(attr1.getNameFormat(), is("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")); + assertThat(attr1.getName(), is("urn:oid:1.3.6.1.4.1.5923.1.1.1.6")); + assertThat(attr1.getFriendlyName(), is("eduPersonPrincipalName")); + assertThat(attr1.getOtherAttributes(), is(Collections.emptyMap())); + assertThat(attr1.getAttributeValue(), is(emptyCollectionOf(Object.class))); + + AttributeType attr2 = aaDescriptor.getAttribute().get(1); + assertThat(attr2.getNameFormat(), is("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")); + assertThat(attr2.getName(), is("urn:oid:1.3.6.1.4.1.5923.1.1.1.1")); + assertThat(attr2.getFriendlyName(), is("eduPersonAffiliation")); + assertThat(attr2.getOtherAttributes(), is(Collections.emptyMap())); + assertThat(attr2.getAttributeValue(), containsInAnyOrder((Object) "member", "student", "faculty", "employee", "staff")); } @Test public void testSaml20MetadataEntityDescriptorSP() throws Exception { - assertParsed("saml20-entity-descriptor-sp.xml", EntityDescriptorType.class); + EntityDescriptorType entityDescriptor = assertParsed("saml20-entity-descriptor-sp.xml", EntityDescriptorType.class); + + assertThat(entityDescriptor.getEntityID(), is("https://ServiceProvider.com/SAML")); + assertThat(entityDescriptor.getValidUntil(), is(nullValue())); + assertThat(entityDescriptor.getCacheDuration(), is(nullValue())); + assertThat(entityDescriptor.getID(), is(nullValue())); + + assertThat(entityDescriptor.getExtensions(), is(nullValue())); + + List descriptors = entityDescriptor.getChoiceType(); + assertThat(descriptors, hasSize(1)); + + // SP Descriptor + SPSSODescriptorType spDescriptor = descriptors.get(0).getDescriptors().get(0).getSpDescriptor(); + assertThat(spDescriptor, is(notNullValue())); + + assertThat(spDescriptor.isAuthnRequestsSigned(), is(true)); + assertThat(spDescriptor.isWantAssertionsSigned(), is(false)); + assertThat(spDescriptor.getProtocolSupportEnumeration(), contains("urn:oasis:names:tc:SAML:2.0:protocol")); + + // Key descriptor + List keyDescriptors = spDescriptor.getKeyDescriptor(); + assertThat(keyDescriptors, hasSize(2)); + + KeyDescriptorType signingKey = keyDescriptors.get(0); + assertThat(signingKey.getUse(), is(KeyTypes.SIGNING)); + assertThat(signingKey.getEncryptionMethod(), is(emptyCollectionOf(EncryptionMethodType.class))); + assertThat(signingKey.getKeyInfo().getElementsByTagName("ds:KeyName").item(0).getTextContent(), is("ServiceProvider.com SSO Key")); + + KeyDescriptorType encryptionKey = keyDescriptors.get(1); + assertThat(encryptionKey.getUse(), is(KeyTypes.ENCRYPTION)); + assertThat(encryptionKey.getKeyInfo().getElementsByTagName("ds:KeyName").item(0).getTextContent(), is("ServiceProvider.com Encrypt Key")); + + List encryptionMethods = encryptionKey.getEncryptionMethod(); + assertThat(encryptionMethods, Matchers.hasSize(1)); + assertThat(encryptionMethods.get(0).getAlgorithm(), is("http://www.w3.org/2001/04/xmlenc#rsa-1_5")); + assertThat(encryptionMethods.get(0).getEncryptionMethod(), is(nullValue())); + + // Single logout services + assertThat(spDescriptor.getSingleLogoutService(), hasSize(2)); + EndpointType singleLS1 = spDescriptor.getSingleLogoutService().get(0); + assertThat(singleLS1.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:SOAP"))); + assertThat(singleLS1.getLocation(), is(URI.create("https://ServiceProvider.com/SAML/SLO/SOAP"))); + assertThat(singleLS1.getResponseLocation(), is(nullValue())); + assertThat(singleLS1.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(singleLS1.getOtherAttributes(), is(Collections.emptyMap())); + + EndpointType singleLS2 = spDescriptor.getSingleLogoutService().get(1); + assertThat(singleLS2.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"))); + assertThat(singleLS2.getLocation(), is(URI.create("https://ServiceProvider.com/SAML/SLO/Browser"))); + assertThat(singleLS2.getResponseLocation(), is(URI.create("https://ServiceProvider.com/SAML/SLO/Response"))); + assertThat(singleLS2.getAny(), is(emptyCollectionOf(Object.class))); + assertThat(singleLS2.getOtherAttributes(), is(Collections.emptyMap())); + + // NameID + assertThat(spDescriptor.getNameIDFormat(), contains("urn:oasis:names:tc:SAML:2.0:nameid-format:transient")); + + // Assertion consumer services + List assertionConsumerServices = spDescriptor.getAssertionConsumerService(); + assertThat(assertionConsumerServices, hasSize(2)); + + IndexedEndpointType assertionCS1 = assertionConsumerServices.get(0); + assertThat(assertionCS1.getIndex(), is(0)); + assertThat(assertionCS1.isIsDefault(), is(true)); + assertThat(assertionCS1.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"))); + assertThat(assertionCS1.getLocation(), is(URI.create("https://ServiceProvider.com/SAML/SSO/Artifact"))); + assertThat(assertionCS1.getResponseLocation(), is(nullValue())); + assertThat(assertionCS1.getOtherAttributes(), is(Collections.emptyMap())); + + IndexedEndpointType assertionCS2 = assertionConsumerServices.get(1); + assertThat(assertionCS2.getIndex(), is(1)); + assertThat(assertionCS2.isIsDefault(), is(nullValue())); + assertThat(assertionCS2.getBinding(), is(URI.create("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"))); + assertThat(assertionCS2.getLocation(), is(URI.create("https://ServiceProvider.com/SAML/SSO/POST"))); + assertThat(assertionCS2.getResponseLocation(), is(nullValue())); + assertThat(assertionCS2.getOtherAttributes(), is(Collections.emptyMap())); + + // Attribute consuming services + List attributeConsumingServices = spDescriptor.getAttributeConsumingService(); + assertThat(attributeConsumingServices, hasSize(1)); + + AttributeConsumingServiceType attributeConsumingService = attributeConsumingServices.get(0); + assertThat(attributeConsumingService.getIndex(), is(0)); + assertThat(attributeConsumingService.getServiceName(), hasSize(1)); + LocalizedNameType servName = attributeConsumingService.getServiceName().get(0); + assertThat(servName.getLang(), is("en")); + assertThat(servName.getValue(), is("Academic Journals R US")); + assertThat(attributeConsumingService.getServiceDescription(), is(emptyCollectionOf(LocalizedNameType.class))); + + List requestedAttributes = attributeConsumingService.getRequestedAttribute(); + assertThat(requestedAttributes, hasSize(1)); + + // Requested attribute + RequestedAttributeType requestedAttribute = requestedAttributes.get(0); + assertThat(requestedAttribute.getNameFormat(), is("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")); + assertThat(requestedAttribute.getName(), is("urn:oid:1.3.6.1.4.1.5923.1.1.1.7")); + assertThat(requestedAttribute.getFriendlyName(), is("eduPersonEntitlement")); + + assertThat(requestedAttribute.getAttributeValue(), hasSize(1)); + assertThat((String) requestedAttribute.getAttributeValue().get(0), is("https://ServiceProvider.com/entitlements/123456789")); + + assertThat(requestedAttribute.getOtherAttributes(), is(Collections.emptyMap())); + + // Organization + assertThat(entityDescriptor.getOrganization().getOrganizationName(), hasSize(1)); + LocalizedNameType orgName = entityDescriptor.getOrganization().getOrganizationName().get(0); + assertThat(orgName.getLang(), is("en")); + assertThat(orgName.getValue(), is("Academic Journals R\n US")); + + assertThat(entityDescriptor.getOrganization().getOrganizationDisplayName(), hasSize(1)); + LocalizedNameType orgDispName = entityDescriptor.getOrganization().getOrganizationDisplayName().get(0); + assertThat(orgDispName.getLang(), is("en")); + assertThat(orgDispName.getValue(), is("Academic Journals R US, a Division of Dirk Corp.")); + + assertThat(entityDescriptor.getOrganization().getOrganizationURL(), hasSize(1)); + LocalizedURIType orgURL = entityDescriptor.getOrganization().getOrganizationURL().get(0); + assertThat(orgURL.getLang(), is("en")); + assertThat(orgURL.getValue(), is(URI.create("https://ServiceProvider.com"))); + } + + @Test + public void testSaml20MetadataEntityDescriptorPDP() throws Exception { + EntityDescriptorType descriptor = assertParsed("saml20-entity-descriptor-pdp.xml", EntityDescriptorType.class); + + assertThat(descriptor.getChoiceType(), Matchers.hasSize(1)); + assertThat(descriptor.getChoiceType().get(0).getDescriptors().get(0).getPdpDescriptor(), is(notNullValue())); + + PDPDescriptorType pdpDescriptor = descriptor.getChoiceType().get(0).getDescriptors().get(0).getPdpDescriptor(); + + assertThat(pdpDescriptor.getKeyDescriptor(), Matchers.hasSize(1)); + + KeyDescriptorType keyDescriptorType = pdpDescriptor.getKeyDescriptor().get(0); + assertThat(keyDescriptorType.getEncryptionMethod(), Matchers.hasSize(1)); + + EncryptionMethodType encryptionMethodType = keyDescriptorType.getEncryptionMethod().get(0); + assertThat(encryptionMethodType.getAlgorithm(), is("http://www.example.com/")); + + EncryptionMethodType.EncryptionMethod encryptionMethod = encryptionMethodType.getEncryptionMethod(); + assertThat(encryptionMethod.getKeySize(), is(BigInteger.ONE)); + assertThat(encryptionMethod.getOAEPparams(), is("GpM7".getBytes())); + + // EndpointType parser already tested so we are not checking further + assertThat(pdpDescriptor.getAuthzService(), Matchers.hasSize(1)); + assertThat(pdpDescriptor.getAssertionIDRequestService(), Matchers.hasSize(1)); + } + + @Test + public void testSaml20MetadataEntityDescriptorAuthnAuthority() throws Exception { + EntityDescriptorType descriptor = assertParsed("saml20-entity-descriptor-authn-authority.xml", EntityDescriptorType.class); + + assertThat(descriptor.getChoiceType(), Matchers.hasSize(1)); + assertThat(descriptor.getChoiceType().get(0).getDescriptors().get(0).getAuthnDescriptor(), is(notNullValue())); + + AuthnAuthorityDescriptorType authnDescriptor = descriptor.getChoiceType().get(0).getDescriptors().get(0).getAuthnDescriptor(); + + assertThat(authnDescriptor.getAssertionIDRequestService(), hasSize(1)); + assertThat(authnDescriptor.getAuthnQueryService(), hasSize(1)); + assertThat(authnDescriptor.getProtocolSupportEnumeration(), containsInAnyOrder("http://www.example.com/", "http://www.example2.com/")); + } + + @Test + public void testSaml20MetadataEntitiesDescriptor() throws Exception { + EntitiesDescriptorType entities = assertParsed("saml20-entities-descriptor.xml", EntitiesDescriptorType.class); + + assertThat(entities.getName(), is("https://your-federation.org/metadata/federation-name.xml")); + assertThat(entities.getID(), is(nullValue())); + assertThat(entities.getCacheDuration(), is(nullValue())); + assertThat(entities.getExtensions(), is(nullValue())); + assertThat(entities.getSignature(), is(nullValue())); + assertThat(entities.getValidUntil(), is(nullValue())); + assertThat(entities.getEntityDescriptor(), hasSize(3)); + assertThat(entities.getEntityDescriptor().get(0), instanceOf(EntityDescriptorType.class)); + assertThat(entities.getEntityDescriptor().get(1), instanceOf(EntityDescriptorType.class)); + assertThat(entities.getEntityDescriptor().get(2), instanceOf(EntitiesDescriptorType.class)); + + EntitiesDescriptorType nestedEntities = (EntitiesDescriptorType) entities.getEntityDescriptor().get(2); + assertThat(nestedEntities.getEntityDescriptor(), hasSize(2)); } @Test @@ -341,6 +700,86 @@ public class SAMLParserTest { assertParsed("saml20-authnrequest-invalid-namespace.xml", AuthnRequestType.class); } + @Test + public void testInvalidEndElement() throws Exception { + thrown.expect(ParsingException.class); + thrown.expectMessage(containsString("The element type \"NameIDFormat\" must be terminated by the matching end-tag \"\".")); + + assertParsed("saml20-entity-descriptor-idp-invalid-end-element.xml", EntityDescriptorType.class); + } + + @Test + public void testMissingRequiredAttributeIDPSSODescriptorType() throws Exception { + testMissingAttribute("IDPSSODescriptorType", "protocolSupportEnumeration"); + } + + @Test + public void testMissingRequiredAttributeSPSSODescriptorType() throws Exception { + testMissingAttribute("SPSSODescriptorType", "protocolSupportEnumeration"); + } + + @Test + public void testMissingRequiredAttributeAttributeAuthorityDescriptorType() throws Exception { + testMissingAttribute("AttributeAuthorityDescriptorType", "protocolSupportEnumeration"); + } + + @Test + public void testMissingRequiredAttributeAuthnAuthorityDescriptorType() throws Exception { + testMissingAttribute("AuthnAuthorityDescriptorType", "protocolSupportEnumeration"); + } + + @Test + public void testMissingRequiredAttributePDPDescriptorType() throws Exception { + testMissingAttribute("PDPDescriptorType", "protocolSupportEnumeration"); + } + + @Test + public void testMissingRequiredAttributeAttributeConsumingServiceType() throws Exception { + testMissingAttribute("AttributeConsumingServiceType", "index"); + } + + @Test + public void testMissingRequiredAttributeAttributeType() throws Exception { + testMissingAttribute("AttributeType", "Name"); + } + + @Test + public void testMissingRequiredAttributeContactType() throws Exception { + testMissingAttribute("ContactType", "contactType"); + } + + @Test + public void testMissingRequiredAttributeEncryptionMethodType() throws Exception { + testMissingAttribute("EncryptionMethodType", "Algorithm"); + } + + @Test + public void testMissingRequiredAttributeEndpointTypeBinding() throws Exception { + testMissingAttribute("EndpointType", "Binding"); + } + + @Test + public void testMissingRequiredAttributeEndpointTypeLocation() throws Exception { + testMissingAttribute("EndpointType", "Location"); + } + + @Test + public void testMissingRequiredAttributeEntityDescriptorType() throws Exception { + testMissingAttribute("EntityDescriptorType", "entityID"); + } + + @Test + public void testMissingRequiredAttributeRequestedAttributeType() throws Exception { + testMissingAttribute("RequestedAttributeType", "Name"); + } + + private void testMissingAttribute(String type, String attributeName) throws Exception { + thrown.expect(ParsingException.class); + thrown.expectMessage(containsString("Parser: Required attribute missing: " + attributeName)); + + assertParsed("missing-attribute/saml20-" + type + "-" + attributeName + ".xml", EntityDescriptorType.class); + } + @Test public void testAuthnRequestScoping() throws Exception { assertParsed("KEYCLOAK-6109-authnrequest-scoping.xml", AuthnRequestType.class); diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeAuthorityDescriptorType-protocolSupportEnumeration.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeAuthorityDescriptorType-protocolSupportEnumeration.xml new file mode 100644 index 0000000000..069decdfb9 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeAuthorityDescriptorType-protocolSupportEnumeration.xml @@ -0,0 +1,19 @@ + + ... + + + + + + Identity Providers R + US + + Identity Providers R US, a Division of Lerxst Corp. + + https://IdentityProvider.com + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeConsumingServiceType-index.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeConsumingServiceType-index.xml new file mode 100644 index 0000000000..af8afb3c06 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeConsumingServiceType-index.xml @@ -0,0 +1,20 @@ + + ... + + + Academic Journals R US + + + https://ServiceProvider.com/entitlements/123456789 + + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeType-Name.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeType-Name.xml new file mode 100644 index 0000000000..efbe38517a --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AttributeType-Name.xml @@ -0,0 +1,13 @@ + + ... + + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AuthnAuthorityDescriptorType-protocolSupportEnumeration.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AuthnAuthorityDescriptorType-protocolSupportEnumeration.xml new file mode 100644 index 0000000000..301bc1eba6 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-AuthnAuthorityDescriptorType-protocolSupportEnumeration.xml @@ -0,0 +1,10 @@ + + ... + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-ContactType-contactType.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-ContactType-contactType.xml new file mode 100644 index 0000000000..b4e12c1c33 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-ContactType-contactType.xml @@ -0,0 +1,18 @@ + + ... + + + + string + string + string + http://www.example.com/ + string + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EncryptionMethodType-Algorithm.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EncryptionMethodType-Algorithm.xml new file mode 100644 index 0000000000..8ce23321d9 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EncryptionMethodType-Algorithm.xml @@ -0,0 +1,19 @@ + + ... + + + + + string + + + 1 + GpM7 + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EndpointType-Binding.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EndpointType-Binding.xml new file mode 100644 index 0000000000..fe1d1862be --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EndpointType-Binding.xml @@ -0,0 +1,12 @@ + + ... + + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EndpointType-Location.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EndpointType-Location.xml new file mode 100644 index 0000000000..8eb5a5b2f7 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EndpointType-Location.xml @@ -0,0 +1,11 @@ + + ... + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EntityDescriptorType-entityID.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EntityDescriptorType-entityID.xml new file mode 100644 index 0000000000..071803da6b --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-EntityDescriptorType-entityID.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-IDPSSODescriptorType-protocolSupportEnumeration.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-IDPSSODescriptorType-protocolSupportEnumeration.xml new file mode 100644 index 0000000000..5a971c959b --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-IDPSSODescriptorType-protocolSupportEnumeration.xml @@ -0,0 +1,9 @@ + + ... + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-PDPDescriptorType-protocolSupportEnumeration.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-PDPDescriptorType-protocolSupportEnumeration.xml new file mode 100644 index 0000000000..c6b2a633fc --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-PDPDescriptorType-protocolSupportEnumeration.xml @@ -0,0 +1,10 @@ + + ... + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-RequestedAttributeType-Name.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-RequestedAttributeType-Name.xml new file mode 100644 index 0000000000..7695618564 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-RequestedAttributeType-Name.xml @@ -0,0 +1,19 @@ + + ... + + + Academic Journals R US + + + https://ServiceProvider.com/entitlements/123456789 + + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-SPSSODescriptorType-protocolSupportEnumeration.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-SPSSODescriptorType-protocolSupportEnumeration.xml new file mode 100644 index 0000000000..492cca823a --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/missing-attribute/saml20-SPSSODescriptorType-protocolSupportEnumeration.xml @@ -0,0 +1,9 @@ + + ... + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entities-descriptor.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entities-descriptor.xml new file mode 100644 index 0000000000..c3b7062887 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entities-descriptor.xml @@ -0,0 +1,240 @@ + + + + + + example.org + + + + + ... + + + + + urn:mace:shibboleth:1.0:nameIdentifier + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + + + + + + + + + + ... + + + + + + + + + urn:mace:shibboleth:1.0:nameIdentifier + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + Your Identities + Your Identities + http://www.example.org/ + + + Your + Contact + admin@example.org + + + + + + + + + + + + + + + + + ... + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:mace:shibboleth:1.0:nameIdentifier + + + + + + + + + + + Your Service + Your Service + http://sp.example.org/ + + + Your + Admin + admin@example.org + + + + + + + + + + example.org + + + + + ... + + + + + urn:mace:shibboleth:1.0:nameIdentifier + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + + + + + + + + + + ... + + + + + + + + + urn:mace:shibboleth:1.0:nameIdentifier + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + Your Identities + Your Identities + http://www.example.org/ + + + Your + Contact + admin@example.org + + + + + + + + + + + + + + + + + ... + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:mace:shibboleth:1.0:nameIdentifier + + + + + + + + + + + Your Service + Your Service + http://sp.example.org/ + + + Your + Admin + admin@example.org + + + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-authn-authority.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-authn-authority.xml new file mode 100644 index 0000000000..47cd9e6147 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-authn-authority.xml @@ -0,0 +1,58 @@ + + ... + + + + ... + ... + ... + + GpM7 + + string + + ... + + ... + + + string + + + 1 + GpM7 + + + + + + string + string + http://www.example.com/ + + + ... + string + string + string + http://www.example.com/ + string + + ... + ... + http://www.example.com/ + + + Identity Providers R + US + + Identity Providers R US, a Division of Lerxst Corp. + + https://IdentityProvider.com + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-idp-invalid-end-element.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-idp-invalid-end-element.xml new file mode 100644 index 0000000000..6d92f62154 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-idp-invalid-end-element.xml @@ -0,0 +1,101 @@ + + ... + + + + IdentityProvider.com SSO Key + + + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + + + member + student + faculty + employee + staff + + + + + + IdentityProvider.com AA Key + + + + + + urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + + + member + student + faculty + employee + staff + + + + Identity Providers R + US + + Identity Providers R US, a Division of Lerxst Corp. + + https://IdentityProvider.com + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-pdp.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-pdp.xml new file mode 100644 index 0000000000..a0518c398d --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-entity-descriptor-pdp.xml @@ -0,0 +1,58 @@ + + ... + + + + ... + ... + ... + + GpM7 + + string + + Any text, intermingled with: + + + + + + string + + + 1 + GpM7 + + + + string + string + http://www.example.com/ + + + string + string + string + http://www.example.com/ + string + + + + + + http://www.example.com/ + + + Identity Providers R + US + + Identity Providers R US, a Division of Lerxst Corp. + + https://IdentityProvider.com + +