From e7cdb8ad548880b7ef7007231802b713de6d2b34 Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Mon, 29 Jan 2018 15:17:43 +0100 Subject: [PATCH] KEYCLOAK-6473 KEYCLOAK-6472 SAML parser refactor + protocol parsers --- .../saml/config/parsers/IDPXmlParser.java | 12 +- .../saml/config/parsers/KeyXmlParser.java | 14 +- .../parsers/KeycloakSamlAdapterXMLParser.java | 6 +- .../saml/config/parsers/KeysXmlParser.java | 10 +- .../saml/config/parsers/SPXmlParser.java | 12 +- .../common/constants/JBossSAMLConstants.java | 342 +++++++++-- .../constants/JBossSAMLURIConstants.java | 195 +++--- saml-core/pom.xml | 3 +- .../saml/SAML2LogoutResponseBuilder.java | 3 +- .../saml/common/DefaultPicketLinkLogger.java | 6 +- .../saml/common/PicketLinkLogger.java | 2 +- .../saml/common/parsers/AbstractParser.java | 74 +-- .../common/parsers/AbstractStaxParser.java | 151 +++++ .../saml/common/parsers/AnyDomParser.java | 64 ++ ...rNamespaceSupport.java => StaxParser.java} | 22 +- .../saml/common/util/StaxParserUtil.java | 212 ++++++- .../saml/common/util/TransformerUtil.java | 26 +- .../api/saml/v2/request/SAML2Request.java | 9 +- .../api/saml/v2/response/SAML2Response.java | 16 +- .../parsers/saml/SAML11AssertionParser.java | 20 +- .../parsers/saml/SAML11RequestParser.java | 12 +- .../parsers/saml/SAML11ResponseParser.java | 16 +- .../parsers/saml/SAML11SubjectParser.java | 17 +- .../saml/SAMLArtifactResolveParser.java | 91 --- .../saml/SAMLArtifactResponseParser.java | 113 ---- .../parsers/saml/SAMLAssertionParser.java | 184 ------ .../saml/SAMLAttributeQueryParser.java | 93 --- .../parsers/saml/SAMLAuthNRequestParser.java | 215 ------- .../parsers/saml/SAMLConditionsParser.java | 173 ------ .../parsers/saml/SAMLExtensionsParser.java | 82 --- .../core/parsers/saml/SAMLParser.java | 151 ++--- .../saml/SAMLRequestAbstractParser.java | 110 ---- .../core/parsers/saml/SAMLResponseParser.java | 110 ---- .../parsers/saml/SAMLSloRequestParser.java | 117 ---- .../parsers/saml/SAMLSloResponseParser.java | 79 --- .../saml/SAMLStatusResponseTypeParser.java | 186 ------ .../core/parsers/saml/SAMLSubjectParser.java | 205 ------- .../AbstractStaxSamlAssertionParser.java | 40 ++ .../saml/assertion/SAMLAssertionParser.java | 103 ++++ .../saml/assertion/SAMLAssertionQNames.java | 116 ++++ .../saml/assertion/SAMLAttributeParser.java | 69 +++ .../SAMLAttributeStatementParser.java | 59 ++ .../assertion/SAMLAttributeValueParser.java | 138 +++++ .../SAMLAudienceRestrictionParser.java | 61 ++ .../assertion/SAMLAuthnContextParser.java | 94 +++ .../assertion/SAMLAuthnStatementParser.java | 76 +++ .../saml/assertion/SAMLConditionsParser.java | 71 +++ .../SAMLEncryptedAssertionParser.java} | 32 +- .../SAMLSubjectConfirmationDataParser.java | 68 +++ .../SAMLSubjectConfirmationParser.java | 71 +++ .../saml/assertion/SAMLSubjectParser.java | 79 +++ .../saml/metadata/AbstractStaxSamlParser.java | 50 ++ .../SAMLEntitiesDescriptorParser.java | 17 +- .../metadata/SAMLEntityDescriptorParser.java | 101 ++-- .../saml/metadata/SAMLExtensionsParser.java | 52 ++ .../SAMLRequestedAttributeParser.java | 80 +++ .../AbstractStaxSamlProtocolParser.java | 40 ++ .../protocol/SAMLArtifactResolveParser.java | 87 +++ .../protocol/SAMLArtifactResponseParser.java | 97 +++ .../protocol/SAMLAttributeQueryParser.java | 83 +++ .../saml/protocol/SAMLAuthNRequestParser.java | 127 ++++ .../saml/protocol/SAMLExtensionsParser.java | 51 ++ .../saml/protocol/SAMLProtocolQNames.java | 125 ++++ .../protocol/SAMLRequestAbstractParser.java | 75 +++ .../SAMLRequestedAuthnContextParser.java | 85 +++ .../saml/protocol/SAMLResponseParser.java | 96 +++ .../saml/protocol/SAMLSloRequestParser.java | 94 +++ .../saml/protocol/SAMLSloResponseParser.java | 88 +++ .../saml/protocol/SAMLStatusCodeParser.java | 59 ++ .../saml/protocol/SAMLStatusParser.java | 78 +++ .../SAMLStatusResponseTypeParser.java | 49 ++ .../xmldsig/AbstractStaxXmlDSigParser.java | 40 ++ .../saml/xmldsig/DsaKeyValueParser.java | 94 +++ .../parsers/saml/xmldsig/KeyInfoParser.java | 78 +++ .../saml/xmldsig/RsaKeyValueParser.java | 64 ++ .../parsers/saml/xmldsig/X509DataParser.java | 66 +++ .../parsers/saml/xmldsig/XmlDSigQNames.java | 96 +++ .../core/parsers/util/HasQName.java | 29 + .../core/parsers/util/QNameEnumLookup.java | 64 ++ .../core/parsers/util/SAML11ParserUtil.java | 90 ++- .../core/parsers/util/SAMLParserUtil.java | 554 +----------------- .../JBossSAMLAuthnResponseFactory.java | 2 +- .../core/saml/v2/util/AssertionUtil.java | 2 +- .../core/saml/v2/util/SignatureUtil.java | 75 --- .../core/saml/v2/writers/BaseWriter.java | 2 +- .../saml/v2/writers/SAMLAssertionWriter.java | 8 +- .../saml/v2/writers/SAMLResponseWriter.java | 2 +- .../saml/SAMLAttributeQueryParserTest.java | 2 +- .../core/parsers/saml/SAMLParserTest.java | 520 ++++++++++++---- ...AK-3971-8859-2-in-header-authnresponse.xml | 1 + ...OAK-3971-utf-8-no-header-authnresponse.xml | 9 +- .../KEYCLOAK-4790-Empty-attribute-value.xml | 29 +- .../parsers/saml/saml20-assertion-advice.xml | 8 +- .../parsers/saml/saml20-assertion-dsakey.xml | 44 ++ .../saml/saml20-assertion-encrypted.xml | 10 + .../parsers/saml/saml20-assertion-example.xml | 15 +- .../saml20-authnrequest-invalid-namespace.xml | 9 + .../saml20-authnrequest-invalid-per-xsd.xml | 10 + .../core/parsers/saml/saml20-authnrequest.xml | 4 + ...l20-encrypted-signed-redirect-response.xml | 2 +- ...l20-logout-response-nested-status-deep.xml | 16 + .../saml20-logout-response-nested-status.xml | 8 + .../saml20-logout-response-status-detail.xml | 13 + .../saml/saml20-logout-response-status.xml | 7 + .../broker/saml/SAMLDataMarshaller.java | 12 +- .../saml/SAMLIdentityProviderFactory.java | 2 +- .../EntityDescriptorDescriptionConverter.java | 2 +- .../profile/ecp/SamlEcpProfileService.java | 2 +- .../broker/saml/SAMLDataMarshallerTest.java | 4 +- .../org/keycloak/testsuite/util/Matchers.java | 4 +- .../keycloak/testsuite/util/SamlClient.java | 4 +- .../matchers/SamlResponseTypeMatcher.java | 2 +- .../util/saml/SamlDocumentStepBuilder.java | 10 +- .../testsuite/admin/IdentityProviderTest.java | 7 +- .../saml/AuthnRequestNameIdFormatTest.java | 13 +- .../keycloak/testsuite/saml/LogoutTest.java | 2 +- .../testsuite/saml/SamlEcpProfileTest.java | 6 +- 117 files changed, 4747 insertions(+), 3060 deletions(-) create mode 100644 saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractStaxParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/common/parsers/AnyDomParser.java rename saml-core/src/main/java/org/keycloak/saml/common/parsers/{ParserNamespaceSupport.java => StaxParser.java} (68%) delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAssertionParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLConditionsParser.java delete mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLExtensionsParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLRequestAbstractParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLStatusResponseTypeParser.java delete mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSubjectParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/AbstractStaxSamlAssertionParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionQNames.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeStatementParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeValueParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAudienceRestrictionParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAuthnContextParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAuthnStatementParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLConditionsParser.java rename saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/{metadata/AbstractDescriptorParser.java => assertion/SAMLEncryptedAssertionParser.java} (50%) mode change 100755 => 100644 create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectConfirmationDataParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectConfirmationParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLExtensionsParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLRequestedAttributeParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/AbstractStaxSamlProtocolParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResolveParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResponseParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLAttributeQueryParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLAuthNRequestParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLExtensionsParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLProtocolQNames.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLRequestAbstractParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLRequestedAuthnContextParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLResponseParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLSloRequestParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLSloResponseParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusCodeParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusParser.java create mode 100755 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusResponseTypeParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/AbstractStaxXmlDSigParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/DsaKeyValueParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/KeyInfoParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/RsaKeyValueParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/X509DataParser.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/XmlDSigQNames.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/HasQName.java create mode 100644 saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/QNameEnumLookup.java create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-dsakey.xml create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-encrypted.xml create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-namespace.xml create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-per-xsd.xml create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status-deep.xml create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status.xml create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status-detail.xml create mode 100644 saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status.xml diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java index 839d4f6abd..6dca7aadf3 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java @@ -61,7 +61,7 @@ public class IDPXmlParser extends AbstractParser { break; if (xmlEvent instanceof EndElement) { EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getEndElementName(endElement); + String endElementName = StaxParserUtil.getElementName(endElement); if (endElementName.equals(ConfigXmlConstants.IDP_ELEMENT)) break; else @@ -70,7 +70,7 @@ public class IDPXmlParser extends AbstractParser { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(ConfigXmlConstants.SINGLE_SIGN_ON_SERVICE_ELEMENT)) { IDP.SingleSignOnService sso = parseSingleSignOnService(xmlEventReader, signaturesRequired); idp.setSingleSignOnService(sso); @@ -142,22 +142,18 @@ public class IDPXmlParser extends AbstractParser { break; if (xmlEvent instanceof EndElement) { EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getEndElementName(endElement); + String endElementName = StaxParserUtil.getElementName(endElement); if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) break; else continue; } - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); StaxParserUtil.bypassElementBlock(xmlEventReader, tag); } return config; } - @Override - public boolean supports(QName qname) { - return false; - } } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java index f14c1df076..258b21adfa 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java @@ -47,16 +47,16 @@ public class KeyXmlParser extends AbstractParser { break; if (xmlEvent instanceof EndElement) { EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getEndElementName(endElement); + String endElementName = StaxParserUtil.getElementName(endElement); if (endElementName.equals(ConfigXmlConstants.KEY_ELEMENT)) break; else - throw logger.parserUnknownEndElement(endElementName); + throw logger.parserUnknownEndElement(endElementName, xmlEvent.getLocation()); } startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) { key.setKeystore(parseKeyStore(xmlEventReader)); } else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) { @@ -100,7 +100,7 @@ public class KeyXmlParser extends AbstractParser { break; if (xmlEvent instanceof EndElement) { EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getEndElementName(endElement); + String endElementName = StaxParserUtil.getElementName(endElement); if (endElementName.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) break; else @@ -109,7 +109,7 @@ public class KeyXmlParser extends AbstractParser { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); keyStore.setCertificateAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR)); @@ -138,8 +138,4 @@ public class KeyXmlParser extends AbstractParser { } - @Override - public boolean supports(QName qname) { - return false; - } } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java index 1330d34a99..b9739388c5 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java @@ -42,7 +42,7 @@ public class KeycloakSamlAdapterXMLParser extends AbstractParser { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(ConfigXmlConstants.SP_ELEMENT)) { SPXmlParser parser = new SPXmlParser(); SP sp = (SP)parser.parse(xmlEventReader); @@ -55,8 +55,4 @@ public class KeycloakSamlAdapterXMLParser extends AbstractParser { return adapter; } - @Override - public boolean supports(QName qname) { - return false; - } } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java index f75331ad5a..13afbdbafa 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java @@ -47,16 +47,16 @@ public class KeysXmlParser extends AbstractParser { break; if (xmlEvent instanceof EndElement) { EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getEndElementName(endElement); + String endElementName = StaxParserUtil.getElementName(endElement); if (endElementName.equals(ConfigXmlConstants.KEYS_ELEMENT)) break; else - throw logger.parserUnknownEndElement(endElementName); + throw logger.parserUnknownEndElement(endElementName, xmlEvent.getLocation()); } startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(ConfigXmlConstants.KEY_ELEMENT)) { KeyXmlParser parser = new KeyXmlParser(); Key key = (Key)parser.parse(xmlEventReader); @@ -69,8 +69,4 @@ public class KeysXmlParser extends AbstractParser { return keys; } - @Override - public boolean supports(QName qname) { - return false; - } } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java index 57d5f58f62..45739870dd 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java @@ -97,7 +97,7 @@ public class SPXmlParser extends AbstractParser { break; if (xmlEvent instanceof EndElement) { EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getEndElementName(endElement); + String endElementName = StaxParserUtil.getElementName(endElement); if (endElementName.equals(ConfigXmlConstants.SP_ELEMENT)) break; else @@ -106,7 +106,7 @@ public class SPXmlParser extends AbstractParser { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) { KeysXmlParser parser = new KeysXmlParser(); List keys = (List) parser.parse(xmlEventReader); @@ -148,7 +148,7 @@ public class SPXmlParser extends AbstractParser { break; if (xmlEvent instanceof EndElement) { EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getEndElementName(endElement); + String endElementName = StaxParserUtil.getElementName(endElement); if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) break; else @@ -157,7 +157,7 @@ public class SPXmlParser extends AbstractParser { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); String attributeValue = getAttributeValue(element, ConfigXmlConstants.NAME_ATTR); @@ -174,8 +174,4 @@ public class SPXmlParser extends AbstractParser { sp.setRoleAttributes(roleAttributes); } - @Override - public boolean supports(QName qname) { - return false; - } } diff --git a/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java b/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java index 13deac5097..2615e58b2b 100755 --- a/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java +++ b/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java @@ -16,65 +16,309 @@ */ package org.keycloak.saml.common.constants; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.xml.namespace.QName; +import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.*; + /** * SAML Constants * - * @author Anil.Saldhana@redhat.com * @since Dec 10, 2008 */ public enum JBossSAMLConstants { - ADDRESS("Address"), ADVICE("Advice"), ADDITIONAL_METADATA_LOCATION("AdditionalMetadataLocation"), AFFILIATION_DESCRIPTOR( - "AffiliationDescriptor"), ALLOW_CREATE("AllowCreate"), ARTIFACT("Artifact"), ARTIFACT_RESOLVE("ArtifactResolve"), ARTIFACT_RESPONSE( - "ArtifactResponse"), ARTIFACT_RESOLUTION_SERVICE("ArtifactResolutionService"), ASSERTION("Assertion"), ASSERTION_CONSUMER_SERVICE( - "AssertionConsumerService"), ASSERTION_CONSUMER_SERVICE_URL("AssertionConsumerServiceURL"), ASSERTION_CONSUMER_SERVICE_INDEX( - "AssertionConsumerServiceIndex"), ASSERTION_ID_REQUEST_SERVICE("AssertionIDRequestService"), ATTRIBUTE("Attribute"), ATTRIBUTE_QUERY( - "AttributeQuery"), ATTRIBUTE_AUTHORITY_DESCRIPTOR("AttributeAuthorityDescriptor"), ATTRIBUTE_CONSUMING_SERVICE( - "AttributeConsumingService"), ATTRIBUTE_CONSUMING_SERVICE_INDEX("AttributeConsumingServiceIndex"), ATTRIBUTE_PROFILE("AttributeProfile"), ATTRIBUTE_SERVICE( - "AttributeService"), ATTRIBUTE_STATEMENT("AttributeStatement"), ATTRIBUTE_VALUE("AttributeValue"), AUDIENCE( - "Audience"), AUDIENCE_RESTRICTION("AudienceRestriction"), AUTHN_CONTEXT("AuthnContext"), AUTHENTICATING_AUTHORITY( - "AuthenticatingAuthority"), AUTHN_AUTHORITY_DESCRIPTOR("AuthnAuthorityDescriptor"), AUTHN_CONTEXT_CLASS_REF( - "AuthnContextClassRef"), AUTHN_CONTEXT_DECLARATION("AuthnContextDecl"), AUTHN_CONTEXT_DECLARATION_REF( - "AuthnContextDeclRef"), AUTHN_INSTANT("AuthnInstant"), AUTHN_REQUEST("AuthnRequest"), AUTHN_STATEMENT( - "AuthnStatement"), AUTHN_REQUESTS_SIGNED("AuthnRequestsSigned"), BASEID("BaseID"), BINDING("Binding"), CACHE_DURATION( - "cacheDuration"), COMPANY("Company"), CONDITIONS("Conditions"), COMPARISON("Comparison"), CONSENT("Consent"), CONTACT_PERSON("ContactPerson"), CONTACT_TYPE( - "contactType"), DESTINATION("Destination"), DNS_NAME("DNSName"), EMAIL_ADDRESS("EmailAddress"), ENCODING("Encoding"), ENCRYPTED_ASSERTION( - "EncryptedAssertion"), ENCRYPTED_ID("EncryptedID"), ENTITY_ID("entityID"), ENTITY_DESCRIPTOR("EntityDescriptor"), ENTITIES_DESCRIPTOR( - "EntitiesDescriptor"), EXTENSIONS("Extensions"), FORMAT("Format"), FRIENDLY_NAME("FriendlyName"), FORCE_AUTHN( - "ForceAuthn"), GIVEN_NAME("GivenName"), ID("ID"), IDP_SSO_DESCRIPTOR("IDPSSODescriptor"), INDEX("index"), INPUT_CONTEXT_ONLY( - "InputContextOnly"), IN_RESPONSE_TO("InResponseTo"), ISDEFAULT("isDefault"), IS_REQUIRED("isRequired"), IS_PASSIVE( - "IsPassive"), ISSUE_INSTANT("IssueInstant"), ISSUER("Issuer"), KEY_DESCRIPTOR("KeyDescriptor"), KEY_INFO("KeyInfo"), ENCRYPTION_METHOD("EncryptionMethod"), LANG( - "lang"), LANG_EN("en"), LOCATION("Location"), LOGOUT_REQUEST("LogoutRequest"), LOGOUT_RESPONSE("LogoutResponse"), MANAGE_NAMEID_SERVICE( - "ManageNameIDService"), METADATA_MIME("application/samlmetadata+xml"), METHOD("Method"), NAME("Name"), NAME_FORMAT( - "NameFormat"), NAMEID("NameID"), NAMEID_FORMAT("NameIDFormat"), NAMEID_MAPPING_SERVICE("NameIDMappingService"), NAMEID_POLICY( - "NameIDPolicy"), NAME_QUALIFIER("NameQualifier"), NOT_BEFORE("NotBefore"), NOT_ON_OR_AFTER("NotOnOrAfter"), ORGANIZATION( - "Organization"), ORGANIZATION_NAME("OrganizationName"), ORGANIZATION_DISPLAY_NAME("OrganizationDisplayName"), ORGANIZATION_URL( - "OrganizationURL"), ORGANIZATION_URL_ALT( - "OrganizationUrl"), PDP_DESCRIPTOR("PDPDescriptor"), PROTOCOL_BINDING("ProtocolBinding"), PROTOCOL_SUPPORT_ENUMERATION( - "protocolSupportEnumeration"), PROVIDER_NAME("ProviderName"), REQUESTED_AUTHN_CONTEXT("RequestedAuthnContext"), REASON( - "Reason"), RECIPIENT("Recipient"), REQUEST("Request"), REQUESTED_ATTRIBUTE("RequestedAttribute"), REQUEST_ABSTRACT( - "RequestAbstract"), RESPONSE("Response"), RESPONSE_LOCATION("ResponseLocation"), RETURN_CONTEXT("ReturnContext"), SCOPING("Scoping"), SESSION_INDEX( - "SessionIndex"), SERVICE_NAME("ServiceName"), SERVICE_DESCRIPTION("ServiceDescription"), SP_PROVIDED_ID( - "SPProvidedID"), SP_NAME_QUALIFIER("SPNameQualifier"), SP_SSO_DESCRIPTOR("SPSSODescriptor"), SIGNATURE("Signature"), SIGNATURE_SHA1_WITH_DSA( - "http://www.w3.org/2000/09/xmldsig#dsa-sha1"), SIGNATURE_SHA1_WITH_RSA("http://www.w3.org/2000/09/xmldsig#rsa-sha1"), SINGLE_SIGNON_SERVICE( - "SingleSignOnService"), SINGLE_LOGOUT_SERVICE("SingleLogoutService"), STATEMENT("Statement"), STATUS("Status"), STATUS_CODE( - "StatusCode"), STATUS_DETAIL("StatusDetail"), STATUS_MESSAGE("StatusMessage"), STATUS_RESPONSE_TYPE( - "StatusResponseType"), SUBJECT("Subject"), SUBJECT_CONFIRMATION("SubjectConfirmation"), SUBJECT_CONFIRMATION_DATA( - "SubjectConfirmationData"), SUBJECT_LOCALITY("SubjectLocality"), SURNAME("SurName"), TELEPHONE_NUMBER( - "TelephoneNumber"), TYPE("type"), USE("use"), VALUE("Value"), VALID_UNTIL("validUntil"), VERSION("Version"), VERSION_2_0( - "2.0"), WANT_AUTHN_REQUESTS_SIGNED("WantAuthnRequestsSigned"), WANT_ASSERTIONS_SIGNED("WantAssertionsSigned"), XACML_AUTHZ_DECISION_QUERY( - "XACMLAuthzDecisionQuery"), XACML_AUTHZ_DECISION_QUERY_TYPE("XACMLAuthzDecisionQueryType"), XACML_AUTHZ_DECISION_STATEMENT_TYPE( - "XACMLAuthzDecisionStatementType"), HTTP_POST_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"), ONE_TIME_USE ("OneTimeUse"), - UNSOLICITED_RESPONSE_TARGET("TARGET"), UNSOLICITED_RESPONSE_SAML_VERSION("SAML_VERSION"), UNSOLICITED_RESPONSE_SAML_BINDING("SAML_BINDING"), - ROLE_DESCRIPTOR("RoleDescriptor"), - REQUEST_AUTHENTICATED("RequestAuthenticated"); + // saml-schema-protocol-2.0.xsd + ARTIFACT(PROTOCOL_NSURI, "Artifact"), + ARTIFACT_RESOLVE(PROTOCOL_NSURI, "ArtifactResolve"), + ARTIFACT_RESPONSE(PROTOCOL_NSURI, "ArtifactResponse"), + ASSERTION_ID_REQUEST(PROTOCOL_NSURI, "AssertionIDRequest"), + ATTRIBUTE_QUERY(PROTOCOL_NSURI, "AttributeQuery"), + AUTHN_QUERY(PROTOCOL_NSURI, "AuthnQuery"), + AUTHN_REQUEST(PROTOCOL_NSURI, "AuthnRequest"), + AUTHZ_DECISION_QUERY(PROTOCOL_NSURI, "AuthzDecisionQuery"), + EXTENSIONS__PROTOCOL(PROTOCOL_NSURI, "Extensions"), + GET_COMPLETE(PROTOCOL_NSURI, "GetComplete"), + IDP_ENTRY(PROTOCOL_NSURI, "IDPEntry"), + IDP_LIST(PROTOCOL_NSURI, "IDPList"), + LOGOUT_REQUEST(PROTOCOL_NSURI, "LogoutRequest"), + LOGOUT_RESPONSE(PROTOCOL_NSURI, "LogoutResponse"), + MANAGE_NAMEID_REQUEST(PROTOCOL_NSURI, "ManageNameIDRequest"), + MANAGE_NAMEID_RESPONSE(PROTOCOL_NSURI, "ManageNameIDResponse"), + NAMEID_MAPPING_REQUEST(PROTOCOL_NSURI, "NameIDMappingRequest"), + NAMEID_MAPPING_RESPONSE(PROTOCOL_NSURI, "NameIDMappingResponse"), + NAMEID_POLICY(PROTOCOL_NSURI, "NameIDPolicy"), + NEW_ENCRYPTEDID(PROTOCOL_NSURI, "NewEncryptedID"), + NEWID(PROTOCOL_NSURI, "NewID"), + REQUESTED_AUTHN_CONTEXT(PROTOCOL_NSURI, "RequestedAuthnContext"), + REQUESTERID(PROTOCOL_NSURI, "RequesterID"), + RESPONSE__PROTOCOL(PROTOCOL_NSURI, "Response"), + SCOPING(PROTOCOL_NSURI, "Scoping"), + SESSION_INDEX(PROTOCOL_NSURI, "SessionIndex"), + STATUS_CODE(PROTOCOL_NSURI, "StatusCode"), + STATUS_DETAIL(PROTOCOL_NSURI, "StatusDetail"), + STATUS_MESSAGE(PROTOCOL_NSURI, "StatusMessage"), + STATUS(PROTOCOL_NSURI, "Status"), + SUBJECT_QUERY(PROTOCOL_NSURI, "SubjectQuery"), + TERMINATE(PROTOCOL_NSURI, "Terminate"), - private String name; + // saml-schema-assertion-2.0.xsd + ACTION(ASSERTION_NSURI, "Action"), + ADVICE(ASSERTION_NSURI, "Advice"), + ASSERTION(ASSERTION_NSURI, "Assertion"), + ASSERTION_ID_REF(ASSERTION_NSURI, "AssertionIDRef"), + ASSERTION_URI_REF(ASSERTION_NSURI, "AssertionURIRef"), + ATTRIBUTE(ASSERTION_NSURI, "Attribute"), + ATTRIBUTE_STATEMENT(ASSERTION_NSURI, "AttributeStatement"), + ATTRIBUTE_VALUE(ASSERTION_NSURI, "AttributeValue"), + AUDIENCE(ASSERTION_NSURI, "Audience"), + AUDIENCE_RESTRICTION(ASSERTION_NSURI, "AudienceRestriction"), + AUTHENTICATING_AUTHORITY(ASSERTION_NSURI, "AuthenticatingAuthority"), + AUTHN_CONTEXT(ASSERTION_NSURI, "AuthnContext"), + AUTHN_CONTEXT_CLASS_REF(ASSERTION_NSURI, "AuthnContextClassRef"), + AUTHN_CONTEXT_DECL(ASSERTION_NSURI, "AuthnContextDecl"), + AUTHN_CONTEXT_DECL_REF(ASSERTION_NSURI, "AuthnContextDeclRef"), + AUTHN_STATEMENT(ASSERTION_NSURI, "AuthnStatement"), + AUTHZ_DECISION_STATEMENT(ASSERTION_NSURI, "AuthzDecisionStatement"), + BASEID(ASSERTION_NSURI, "BaseID"), + CONDITION(ASSERTION_NSURI, "Condition"), + CONDITIONS(ASSERTION_NSURI, "Conditions"), + ENCRYPTED_ASSERTION(ASSERTION_NSURI, "EncryptedAssertion"), + ENCRYPTED_ATTRIBUTE(ASSERTION_NSURI, "EncryptedAttribute"), + ENCRYPTED_ID(ASSERTION_NSURI, "EncryptedID"), + EVIDENCE(ASSERTION_NSURI, "Evidence"), + ISSUER(ASSERTION_NSURI, "Issuer"), + NAMEID(ASSERTION_NSURI, "NameID"), + ONE_TIME_USE(ASSERTION_NSURI, "OneTimeUse"), + PROXY_RESTRICTION(ASSERTION_NSURI, "ProxyRestriction"), + STATEMENT(ASSERTION_NSURI, "Statement"), + SUBJECT_CONFIRMATION_DATA(ASSERTION_NSURI, "SubjectConfirmationData"), + SUBJECT_CONFIRMATION(ASSERTION_NSURI, "SubjectConfirmation"), + SUBJECT_LOCALITY(ASSERTION_NSURI, "SubjectLocality"), + SUBJECT(ASSERTION_NSURI, "Subject"), - private JBossSAMLConstants(String val) { - this.name = val; + // saml-schema-metadata-2.0.xsd + ADDITIONAL_METADATA_LOCATION(METADATA_NSURI, "AdditionalMetadataLocation"), + AFFILIATE_MEMBER(METADATA_NSURI, "AffiliateMember"), + AFFILIATION_DESCRIPTOR(METADATA_NSURI, "AffiliationDescriptor"), + ARTIFACT_RESOLUTION_SERVICE(METADATA_NSURI, "ArtifactResolutionService"), + ASSERTION_CONSUMER_SERVICE(METADATA_NSURI, "AssertionConsumerService"), + ASSERTION_ID_REQUEST_SERVICE(METADATA_NSURI, "AssertionIDRequestService"), + ATTRIBUTE_AUTHORITY_DESCRIPTOR(METADATA_NSURI, "AttributeAuthorityDescriptor"), + ATTRIBUTE_CONSUMING_SERVICE(METADATA_NSURI, "AttributeConsumingService"), + ATTRIBUTE_PROFILE(METADATA_NSURI, "AttributeProfile"), + ATTRIBUTE_SERVICE(METADATA_NSURI, "AttributeService"), + AUTHN_AUTHORITY_DESCRIPTOR(METADATA_NSURI, "AuthnAuthorityDescriptor"), + AUTHN_QUERY_SERVICE(METADATA_NSURI, "AuthnQueryService"), + AUTHZ_SERVICE(METADATA_NSURI, "AuthzService"), + COMPANY(METADATA_NSURI, "Company"), + CONTACT_PERSON(METADATA_NSURI, "ContactPerson"), + EMAIL_ADDRESS(METADATA_NSURI, "EmailAddress"), + ENCRYPTION_METHOD(METADATA_NSURI, "EncryptionMethod"), + ENTITIES_DESCRIPTOR(METADATA_NSURI, "EntitiesDescriptor"), + ENTITY_DESCRIPTOR(METADATA_NSURI, "EntityDescriptor"), + EXTENSIONS__METADATA(METADATA_NSURI, "Extensions"), + GIVEN_NAME(METADATA_NSURI, "GivenName"), + IDP_SSO_DESCRIPTOR(METADATA_NSURI, "IDPSSODescriptor"), + KEY_DESCRIPTOR(METADATA_NSURI, "KeyDescriptor"), + MANAGE_NAMEID_SERVICE(METADATA_NSURI, "ManageNameIDService"), + NAMEID_FORMAT(METADATA_NSURI, "NameIDFormat"), + NAMEID_MAPPING_SERVICE(METADATA_NSURI, "NameIDMappingService"), + ORGANIZATION_DISPLAY_NAME(METADATA_NSURI, "OrganizationDisplayName"), + ORGANIZATION_NAME(METADATA_NSURI, "OrganizationName"), + ORGANIZATION(METADATA_NSURI, "Organization"), + ORGANIZATION_URL(METADATA_NSURI, "OrganizationURL"), + ORGANIZATION_URL_ALT(METADATA_NSURI, "OrganizationUrl"), // non-standard: KEYCLOAK-4040 + PDP_DESCRIPTOR(METADATA_NSURI, "PDPDescriptor"), + REQUESTED_ATTRIBUTE(METADATA_NSURI, "RequestedAttribute"), + ROLE_DESCRIPTOR(METADATA_NSURI, "RoleDescriptor"), + SERVICE_DESCRIPTION(METADATA_NSURI, "ServiceDescription"), + SERVICE_NAME(METADATA_NSURI, "ServiceName"), + SINGLE_LOGOUT_SERVICE(METADATA_NSURI, "SingleLogoutService"), + SINGLE_SIGNON_SERVICE(METADATA_NSURI, "SingleSignOnService"), + SP_SSO_DESCRIPTOR(METADATA_NSURI, "SPSSODescriptor"), + SURNAME(METADATA_NSURI, "SurName"), + TELEPHONE_NUMBER(METADATA_NSURI, "TelephoneNumber"), + + // saml-schema-ecp-2.0.xsd + RELAY_STATE(ECP_PROFILE, "RelayState"), + REQUEST(ECP_PROFILE, "Request"), + RESPONSE__ECP(ECP_PROFILE, "Response"), + + SIGNATURE(XMLDSIG_NSURI, "Signature"), + DSA_KEY_VALUE(XMLDSIG_NSURI, "DSAKeyValue"), + KEY_INFO(XMLDSIG_NSURI, "KeyInfo"), + KEY_VALUE(XMLDSIG_NSURI, "KeyValue"), + RSA_KEY_VALUE(XMLDSIG_NSURI, "RSAKeyValue"), + X509_CERT(XMLDSIG_NSURI, "X509Certificate"), + X509_DATA(XMLDSIG_NSURI, "X509Data"), + + // Attribute names and other constants + ADDRESS("Address"), + ALLOW_CREATE("AllowCreate"), + ASSERTION_CONSUMER_SERVICE_URL("AssertionConsumerServiceURL"), + ASSERTION_CONSUMER_SERVICE_INDEX("AssertionConsumerServiceIndex"), + ATTRIBUTE_CONSUMING_SERVICE_INDEX("AttributeConsumingServiceIndex"), + AUTHN_INSTANT("AuthnInstant"), + AUTHN_REQUESTS_SIGNED("AuthnRequestsSigned"), + BINDING("Binding"), + CACHE_DURATION("cacheDuration"), + COMPARISON("Comparison"), + CONSENT("Consent"), + CONTACT_TYPE("contactType"), + DESTINATION("Destination"), + DNS_NAME("DNSName"), + ENCODING("Encoding"), + ENCRYPTED_KEY("EncryptedKey"), + ENTITY_ID("entityID"), + FORMAT("Format"), + FRIENDLY_NAME("FriendlyName"), + FORCE_AUTHN("ForceAuthn"), + ID("ID"), + INDEX("index"), + INPUT_CONTEXT_ONLY("InputContextOnly"), + IN_RESPONSE_TO("InResponseTo"), + ISDEFAULT("isDefault"), + IS_REQUIRED("isRequired"), + IS_PASSIVE("IsPassive"), + ISSUE_INSTANT("IssueInstant"), + LOCATION("Location"), + METHOD("Method"), + NAME("Name"), + NAME_FORMAT("NameFormat"), + NAME_QUALIFIER("NameQualifier"), + NOT_BEFORE("NotBefore"), + NOT_ON_OR_AFTER("NotOnOrAfter"), + PROTOCOL_BINDING("ProtocolBinding"), + PROTOCOL_SUPPORT_ENUMERATION("protocolSupportEnumeration"), + PROVIDER_NAME("ProviderName"), + REASON("Reason"), + RECIPIENT("Recipient"), + REQUEST_ABSTRACT("RequestAbstract"), + RESPONSE_LOCATION("ResponseLocation"), + RETURN_CONTEXT("ReturnContext"), + SP_PROVIDED_ID("SPProvidedID"), + SP_NAME_QUALIFIER("SPNameQualifier"), + STATUS_RESPONSE_TYPE("StatusResponseType"), + TYPE("type"), + USE("use"), + VALUE("Value"), + VALID_UNTIL("validUntil"), + VERSION("Version"), + WANT_AUTHN_REQUESTS_SIGNED("WantAuthnRequestsSigned"), + WANT_ASSERTIONS_SIGNED("WantAssertionsSigned"), + XACML_AUTHZ_DECISION_QUERY("XACMLAuthzDecisionQuery"), + XACML_AUTHZ_DECISION_QUERY_TYPE("XACMLAuthzDecisionQueryType"), + XACML_AUTHZ_DECISION_STATEMENT_TYPE("XACMLAuthzDecisionStatementType"), + REQUEST_AUTHENTICATED("RequestAuthenticated"), + + UNSOLICITED_RESPONSE_TARGET("TARGET"), + UNSOLICITED_RESPONSE_SAML_VERSION("SAML_VERSION"), + UNSOLICITED_RESPONSE_SAML_BINDING("SAML_BINDING"), + + LANG("lang"), + LANG_EN("en"), + METADATA_MIME("application/samlmetadata+xml"), + SIGNATURE_SHA1_WITH_DSA("http://www.w3.org/2000/09/xmldsig#dsa-sha1"), + SIGNATURE_SHA1_WITH_RSA("http://www.w3.org/2000/09/xmldsig#rsa-sha1"), + VERSION_2_0("2.0"), + + /** @deprecated Use namespace-aware variant instead */ + RESPONSE("Response"), + /** @deprecated Use namespace-aware variant instead */ + EXTENSIONS("Extensions"), + + UNKNOWN_VALUE(null) + ; + + private final QName asQName; + private final JBossSAMLURIConstants nsUri; + + private static class ReverseLookup { + // Private class to make sure JBossSAMLURIConstants is fully initialized + private static final Map QNAME_CONSTANTS; + private static final Map CONSTANTS; + + static { + HashMap q = new HashMap<>(JBossSAMLConstants.values().length); + HashMap m = new HashMap<>(JBossSAMLConstants.values().length); + JBossSAMLConstants old; + for (JBossSAMLConstants c : JBossSAMLConstants.values()) { + if ((old = q.put(c.getAsQName(), c)) != null) { + throw new IllegalStateException("Same name " + c.getAsQName() + " used for two distinct constants: " + c + ", " + old); + } + + String key = c.get(); + if ((old = m.put(key, c)) != null) { +// System.out.println("WARNING: " + old); + if (old != null && c.getAsQName().equals(old.getAsQName())) { + throw new IllegalStateException("Same name " + key + " used for two distinct constants: " + c + ", " + old); + } + m.put(key, null); + } + } + QNAME_CONSTANTS = Collections.unmodifiableMap(q); + CONSTANTS = Collections.unmodifiableMap(m); + } + + public JBossSAMLConstants from(String key) { + return CONSTANTS.get(key); + } + + public JBossSAMLConstants from(QName key) { + return QNAME_CONSTANTS.get(key); + } + } + private static final ReverseLookup REVERSE_LOOKUP = new ReverseLookup(); + + private JBossSAMLConstants(String name) { + this.asQName = name == null ? null : new QName(name); + this.nsUri = null; + } + + private JBossSAMLConstants(JBossSAMLURIConstants namespaceUri, String name) { + this.nsUri = namespaceUri; + this.asQName = name == null ? null : new QName(namespaceUri.get(), name); } public String get() { - return this.name; + return this.asQName == null ? null : this.asQName.getLocalPart(); + } + + public QName getAsQName() { + return asQName; + } + + public JBossSAMLURIConstants getNsUri() { + return nsUri; + } + + /** + * Returns an enum constant based if known for the given {@code key}, or the {@code defaultValue} otherwise. + * @param key + * @return + */ + public static JBossSAMLConstants from(String key, JBossSAMLConstants defaultValue) { + final JBossSAMLConstants res = REVERSE_LOOKUP.from(key); + return res == null ? defaultValue : res; + } + + /** + * Returns an enum constant based if known for the given {@code key}, or the {@code UNKNOWN_VALUE} otherwise. + * @param key + * @return + */ + public static JBossSAMLConstants from(String key) { + return from(key, UNKNOWN_VALUE); + } + + /** + * Returns an enum constant based if known for the given {@code name} (namespace-aware), or the {@code UNKNOWN_VALUE} otherwise. + * @param key + * @return + */ + public static JBossSAMLConstants from(QName name) { + final JBossSAMLConstants res = REVERSE_LOOKUP.from(name); + return res == null ? UNKNOWN_VALUE : res; } } \ No newline at end of file diff --git a/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java b/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java index 147949514e..512fecd5fa 100755 --- a/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java +++ b/saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLURIConstants.java @@ -16,6 +16,11 @@ */ package org.keycloak.saml.common.constants; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + /** * Define the constants based on URI * @@ -23,60 +28,58 @@ package org.keycloak.saml.common.constants; * @since Dec 10, 2008 */ public enum JBossSAMLURIConstants { - AC_PASSWORD_PROTECTED_TRANSPORT("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"), AC_PASSWORD( - "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"), AC_TLS_CLIENT( - "urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient"), AC_PREVIOUS_SESSION( - "urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession"), AC_UNSPECIFIED( - "urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"), AC_IP( - "urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol"), + AC_PASSWORD_PROTECTED_TRANSPORT("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"), + AC_PASSWORD("urn:oasis:names:tc:SAML:2.0:ac:classes:Password"), + AC_TLS_CLIENT("urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient"), + AC_PREVIOUS_SESSION("urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession"), + AC_UNSPECIFIED("urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified"), + AC_IP("urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol"), - ASSERTION_NSURI("urn:oasis:names:tc:SAML:2.0:assertion"), ATTRIBUTE_FORMAT_BASIC( - "urn:oasis:names:tc:SAML:2.0:attrname-format:basic"), ATTRIBUTE_FORMAT_URI( - "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"), + ASSERTION_NSURI("urn:oasis:names:tc:SAML:2.0:assertion"), + ATTRIBUTE_FORMAT_BASIC("urn:oasis:names:tc:SAML:2.0:attrname-format:basic"), + ATTRIBUTE_FORMAT_URI("urn:oasis:names:tc:SAML:2.0:attrname-format:uri"), - BEARER("urn:oasis:names:tc:SAML:2.0:cm:bearer"), - - CLAIMS_EMAIL_ADDRESS_2005("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"), CLAIMS_EMAIL_ADDRESS( - "http://schemas.xmlsoap.org/claims/EmailAddress"), CLAIMS_GIVEN_NAME( - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"), CLAIMS_NAME( - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"), CLAIMS_USER_PRINCIPAL_NAME_2005( - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"), CLAIMS_USER_PRINCIPAL_NAME( - "http://schemas.xmlsoap.org/claims/UPN"), CLAIMS_COMMON_NAME("http://schemas.xmlsoap.org/claims/CommonName"), CLAIMS_GROUP( - "http://schemas.xmlsoap.org/claims/Group"), CLAIMS_ROLE( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"), CLAIMS_SURNAME( - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"), CLAIMS_PRIVATE_ID( - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier"), CLAIMS_NAME_IDENTIFIER( - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"), CLAIMS_AUTHENTICATION_METHOD( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod"), CLAIMS_DENY_ONLY_GROUP_SID( - "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid"), CLAIMS_DENY_ONLY_PRIMARY_SID( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid"), CLAIMS_DENY_ONLY_PRIMARY_GROUP_SID( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid"), CLAIMS_GROUP_SID( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid"), CLAIMS_PRIMARY_GROUP_SID( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid"), CLAIMS_PRIMARY_SID( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"), CLAIMS_WINDOWS_ACCOUNT_NAME( - "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"), CLAIMS_PUID( - "http://schemas.xmlsoap.org/claims/PUID"), + CLAIMS_EMAIL_ADDRESS_2005("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"), + CLAIMS_EMAIL_ADDRESS("http://schemas.xmlsoap.org/claims/EmailAddress"), + CLAIMS_GIVEN_NAME("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"), + CLAIMS_NAME("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"), + CLAIMS_USER_PRINCIPAL_NAME_2005("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn"), + CLAIMS_USER_PRINCIPAL_NAME("http://schemas.xmlsoap.org/claims/UPN"), + CLAIMS_COMMON_NAME("http://schemas.xmlsoap.org/claims/CommonName"), + CLAIMS_GROUP("http://schemas.xmlsoap.org/claims/Group"), + CLAIMS_ROLE("http://schemas.microsoft.com/ws/2008/06/identity/claims/role"), + CLAIMS_SURNAME("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"), + CLAIMS_PRIVATE_ID("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier"), + CLAIMS_NAME_IDENTIFIER("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"), + CLAIMS_AUTHENTICATION_METHOD("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod"), + CLAIMS_DENY_ONLY_GROUP_SID("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid"), + CLAIMS_DENY_ONLY_PRIMARY_SID("http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarysid"), + CLAIMS_DENY_ONLY_PRIMARY_GROUP_SID("http://schemas.microsoft.com/ws/2008/06/identity/claims/denyonlyprimarygroupsid"), + CLAIMS_GROUP_SID("http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid"), + CLAIMS_PRIMARY_GROUP_SID("http://schemas.microsoft.com/ws/2008/06/identity/claims/primarygroupsid"), + CLAIMS_PRIMARY_SID("http://schemas.microsoft.com/ws/2008/06/identity/claims/primarysid"), + CLAIMS_WINDOWS_ACCOUNT_NAME("http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"), + CLAIMS_PUID("http://schemas.xmlsoap.org/claims/PUID"), HOLDER_OF_KEY("urn:oasis:names:tc:SAML:2.0:cm:holder-of-key"), - METADATA_NSURI("urn:oasis:names:tc:SAML:2.0:metadata"), METADATA_HTTP_REDIRECT_BINDING( - "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"), + METADATA_NSURI("urn:oasis:names:tc:SAML:2.0:metadata"), - NAMEID_FORMAT_TRANSIENT("urn:oasis:names:tc:SAML:2.0:nameid-format:transient"), NAMEID_FORMAT_PERSISTENT( - "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"), NAMEID_FORMAT_UNSPECIFIED( - "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"), NAMEID_FORMAT_EMAIL( - "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"), NAMEID_FORMAT_X509SUBJECTNAME( - "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"), NAMEID_FORMAT_WINDOWS_DOMAIN_NAME( - "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"), NAMEID_FORMAT_KERBEROS( - "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos"), NAMEID_FORMAT_ENTITY( - "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"), + NAMEID_FORMAT_TRANSIENT("urn:oasis:names:tc:SAML:2.0:nameid-format:transient"), + NAMEID_FORMAT_PERSISTENT("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"), + NAMEID_FORMAT_UNSPECIFIED("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"), + NAMEID_FORMAT_EMAIL("urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"), + NAMEID_FORMAT_X509SUBJECTNAME("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName"), + NAMEID_FORMAT_WINDOWS_DOMAIN_NAME("urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName"), + NAMEID_FORMAT_KERBEROS("urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos"), + NAMEID_FORMAT_ENTITY("urn:oasis:names:tc:SAML:2.0:nameid-format:entity"), PROTOCOL_NSURI("urn:oasis:names:tc:SAML:2.0:protocol"), ECP_PROFILE("urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp"), PAOS_BINDING("urn:liberty:paos:2003-08"), - SIGNATURE_DSA_SHA1("http://www.w3.org/2000/09/xmldsig#dsa-sha1"), SIGNATURE_RSA_SHA1( - "http://www.w3.org/2000/09/xmldsig#rsa-sha1"), + SIGNATURE_DSA_SHA1("http://www.w3.org/2000/09/xmldsig#dsa-sha1"), + SIGNATURE_RSA_SHA1("http://www.w3.org/2000/09/xmldsig#rsa-sha1"), SAML_HTTP_POST_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"), SAML_HTTP_REDIRECT_BINDING("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"), @@ -87,46 +90,90 @@ public enum JBossSAMLURIConstants { SUBJECT_CONFIRMATION_BEARER("urn:oasis:names:tc:SAML:2.0:cm:bearer"), - STATUS_AUTHNFAILED("urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"), STATUS_INVALID_ATTRNAMEVAL( - "urn:oasis:names:tc:SAML:2.0:status:InvalidAttrnameOrValue"), STATUS_INVALID_NAMEIDPOLICY( - "urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy"), STATUS_NOAUTHN_CTX( - "urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext"), STATUS_NO_AVAILABLE_IDP( - "urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP"), STATUS_NO_PASSIVE( - "urn:oasis:names:tc:SAML:2.0:status:NoPassive"), STATUS_NO_SUPPORTED_IDP( - "urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP"), STATUS_PARTIAL_LOGOUT( - "urn:oasis:names:tc:SAML:2.0:status:PartialLogout"), STATUS_PROXYCOUNT_EXCEEDED( - "urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded"), STATUS_REQUEST_DENIED( - "urn:oasis:names:tc:SAML:2.0:status:RequestDenied"), STATUS_REQUEST_UNSUPPORTED( - "urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported"), STATUS_REQUEST_VERSION_DEPRECATED( - "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated"), STATUS_REQUEST_VERSION_2HIGH( - "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh"), STATUS_REQUEST_VERSION_2LOW( - "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow"), STATUS_RESOURCE_NOT_RECOGNIZED( - "urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized"), STATUS_2MANY_RESPONSES( - "urn:oasis:names:tc:SAML:2.0:status:TooManyResponses"), STATUS_UNKNOWN_ATTR_PROFILE( - "urn:oasis:names:tc:SAML:2.0:status:UnknownAttributeProfile"), STATUS_UNKNOWN_PRINCIPAL( - "urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal"), STATUS_UNSUPPORTED_BINDING( - "urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding"), + STATUS_AUTHNFAILED("urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"), + STATUS_INVALID_ATTRNAMEVAL("urn:oasis:names:tc:SAML:2.0:status:InvalidAttrnameOrValue"), + STATUS_INVALID_NAMEIDPOLICY("urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy"), + STATUS_NOAUTHN_CTX("urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext"), + STATUS_NO_AVAILABLE_IDP("urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP"), + STATUS_NO_PASSIVE("urn:oasis:names:tc:SAML:2.0:status:NoPassive"), + STATUS_NO_SUPPORTED_IDP("urn:oasis:names:tc:SAML:2.0:status:NoSupportedIDP"), + STATUS_PARTIAL_LOGOUT("urn:oasis:names:tc:SAML:2.0:status:PartialLogout"), + STATUS_PROXYCOUNT_EXCEEDED("urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded"), + STATUS_REQUEST_DENIED("urn:oasis:names:tc:SAML:2.0:status:RequestDenied"), + STATUS_REQUEST_UNSUPPORTED("urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported"), + STATUS_REQUEST_VERSION_DEPRECATED("urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated"), + STATUS_REQUEST_VERSION_2HIGH("urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh"), + STATUS_REQUEST_VERSION_2LOW("urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooLow"), + STATUS_RESOURCE_NOT_RECOGNIZED("urn:oasis:names:tc:SAML:2.0:status:ResourceNotRecognized"), + STATUS_2MANY_RESPONSES("urn:oasis:names:tc:SAML:2.0:status:TooManyResponses"), + STATUS_UNKNOWN_ATTR_PROFILE("urn:oasis:names:tc:SAML:2.0:status:UnknownAttributeProfile"), + STATUS_UNKNOWN_PRINCIPAL("urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal"), + STATUS_UNSUPPORTED_BINDING("urn:oasis:names:tc:SAML:2.0:status:UnsupportedBinding"), - STATUS_REQUESTOR("urn:oasis:names:tc:SAML:2.0:status:Requestor"), STATUS_RESPONDER( - "urn:oasis:names:tc:SAML:2.0:status:Responder"), STATUS_SUCCESS("urn:oasis:names:tc:SAML:2.0:status:Success"), STATUS_VERSION_MISMATCH( - "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch"), + STATUS_REQUESTOR("urn:oasis:names:tc:SAML:2.0:status:Requestor"), + STATUS_RESPONDER("urn:oasis:names:tc:SAML:2.0:status:Responder"), + STATUS_SUCCESS("urn:oasis:names:tc:SAML:2.0:status:Success"), + STATUS_VERSION_MISMATCH("urn:oasis:names:tc:SAML:2.0:status:VersionMismatch"), - TRANSFORM_ENVELOPED_SIGNATURE("http://www.w3.org/2000/09/xmldsig#enveloped-signature"), TRANSFORM_C14N_EXCL_OMIT_COMMENTS( - "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"), + TRANSFORM_ENVELOPED_SIGNATURE("http://www.w3.org/2000/09/xmldsig#enveloped-signature"), + TRANSFORM_C14N_EXCL_OMIT_COMMENTS("http://www.w3.org/2001/10/xml-exc-c14n#WithComments"), - XSI_PREFIX("xsi"), X500_PREFIX("x500"), X500_NSURI("urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"), XACML_NSURI( - "urn:oasis:names:tc:xacml:2.0:context:schema:os"), XACML_SAML_NSURI("urn:oasis:xacml:2.0:saml:assertion:schema:os"), XACML_SAML_PROTO_NSURI( - "urn:oasis:xacml:2.0:saml:protocol:schema:os"), XML("http://www.w3.org/XML/1998/namespace"), XMLSCHEMA_NSURI( - "http://www.w3.org/2001/XMLSchema"), XMLDSIG_NSURI("http://www.w3.org/2000/09/xmldsig#"), XMLENC_NSURI( - "http://www.w3.org/2001/04/xmlenc#"), XSI_NSURI("http://www.w3.org/2001/XMLSchema-instance"); + XSI_PREFIX("xsi"), + X500_PREFIX("x500"), + X500_NSURI("urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500"), + XACML_NSURI("urn:oasis:names:tc:xacml:2.0:context:schema:os"), + XACML_SAML_NSURI("urn:oasis:xacml:2.0:saml:assertion:schema:os"), + XACML_SAML_PROTO_NSURI("urn:oasis:xacml:2.0:saml:protocol:schema:os"), + XML("http://www.w3.org/XML/1998/namespace"), + XMLSCHEMA_NSURI("http://www.w3.org/2001/XMLSchema"), + XMLDSIG_NSURI("http://www.w3.org/2000/09/xmldsig#"), + XMLENC_NSURI("http://www.w3.org/2001/04/xmlenc#"), + XSI_NSURI("http://www.w3.org/2001/XMLSchema-instance"), + ; - private String uri = null; + private final String uriStr; + private final URI uri; + + private static class ReverseLookup { + // Private class to make sure JBossSAMLURIConstants is fully initialized + private static final Map CONSTANTS; + + static { + HashMap m = new HashMap<>(JBossSAMLURIConstants.values().length); + JBossSAMLURIConstants old; + for (JBossSAMLURIConstants c : JBossSAMLURIConstants.values()) { + if ((old = m.put(c.get(), c)) != null) { + throw new IllegalStateException("Same name " + c.get() + " used for two constants: " + c + ", " + old); + } + } + CONSTANTS = Collections.unmodifiableMap(m); + } + + public JBossSAMLURIConstants from(String key) { + return CONSTANTS.get(key); + } + } + private static final ReverseLookup REVERSE_LOOKUP = new ReverseLookup(); private JBossSAMLURIConstants(String uristr) { - this.uri = uristr; + this.uriStr = uristr; + this.uri = URI.create(uristr); } public String get() { + return this.uriStr; + } + + public URI getUri() { return this.uri; } + + /** + * Returns an enum constant based if known for the given {@code key}, or {@code null} otherwise. + * @param key + * @return + */ + public static JBossSAMLURIConstants from(String key) { + return REVERSE_LOOKUP.from(key); + } } \ No newline at end of file diff --git a/saml-core/pom.xml b/saml-core/pom.xml index 091408391e..ce8e3406f2 100755 --- a/saml-core/pom.xml +++ b/saml-core/pom.xml @@ -61,8 +61,7 @@ org.hamcrest - hamcrest-core - 1.3 + hamcrest-all test diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java index 4d91e2a24e..fe32bf43b0 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java @@ -30,7 +30,6 @@ import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator; import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Document; -import java.net.URI; import java.util.LinkedList; import java.util.List; import org.keycloak.dom.saml.v2.protocol.ExtensionsType; @@ -73,7 +72,7 @@ public class SAML2LogoutResponseBuilder implements SamlProtocolExtensionsAwareBu // Status StatusType statusType = new StatusType(); StatusCodeType statusCodeType = new StatusCodeType(); - statusCodeType.setValue(URI.create(JBossSAMLURIConstants.STATUS_SUCCESS.get())); + statusCodeType.setValue(JBossSAMLURIConstants.STATUS_SUCCESS.getUri()); statusType.setStatusCode(statusCodeType); statusResponse.setStatus(statusType); diff --git a/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java b/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java index b25b55b517..da4dca5703 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java @@ -345,8 +345,8 @@ public class DefaultPicketLinkLogger implements PicketLinkLogger { *@see org.picketlink.identity.federation.PicketLinkLogger#parserUnknownEndElement(java.lang.String) */ @Override - public RuntimeException parserUnknownEndElement(String endElementName) { - return new RuntimeException(ErrorCodes.UNKNOWN_END_ELEMENT + endElementName); + public RuntimeException parserUnknownEndElement(String endElementName, Location location) { + return new RuntimeException(ErrorCodes.UNKNOWN_END_ELEMENT + endElementName + "::location=" + location); } /* @@ -447,7 +447,7 @@ public class DefaultPicketLinkLogger implements PicketLinkLogger { */ @Override public RuntimeException parserExpectedTag(String tag, String foundElementTag) { - return new RuntimeException(ErrorCodes.EXPECTED_TAG + tag + ">. Found <" + foundElementTag + ">"); + return new RuntimeException(ErrorCodes.EXPECTED_TAG + tag + ". Found <" + foundElementTag + ">"); } @Override diff --git a/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java b/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java index 91f2f54758..b8a26421d2 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java @@ -225,7 +225,7 @@ public interface PicketLinkLogger { * * @return */ - RuntimeException parserUnknownEndElement(String endElementName); + RuntimeException parserUnknownEndElement(String endElementName, Location location); /** * @param tag diff --git a/saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractParser.java b/saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractParser.java index f2fa8453a6..1846b371ee 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractParser.java @@ -33,6 +33,9 @@ import javax.xml.stream.events.Characters; import javax.xml.stream.events.XMLEvent; import javax.xml.stream.util.EventReaderDelegate; import java.io.InputStream; +import java.util.regex.Pattern; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; import javax.xml.transform.Source; import javax.xml.transform.dom.DOMSource; import org.w3c.dom.Node; @@ -43,7 +46,7 @@ import org.w3c.dom.Node; * @author Anil.Saldhana@redhat.com * @since Oct 12, 2010 */ -public abstract class AbstractParser implements ParserNamespaceSupport { +public abstract class AbstractParser implements StaxParser { protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); @@ -78,15 +81,15 @@ public abstract class AbstractParser implements ParserNamespaceSupport { /** * Parse an InputStream for payload * - * @param configStream + * @param stream * * @return * * @throws {@link IllegalArgumentException} * @throws {@link IllegalArgumentException} when the configStream is null */ - public Object parse(InputStream configStream) throws ParsingException { - XMLEventReader xmlEventReader = createEventReader(configStream); + public Object parse(InputStream stream) throws ParsingException { + XMLEventReader xmlEventReader = createEventReader(stream); return parse(xmlEventReader); } @@ -99,19 +102,13 @@ public abstract class AbstractParser implements ParserNamespaceSupport { return parse(new DOMSource(node)); } - public XMLEventReader createEventReader(InputStream configStream) throws ParsingException { + public static XMLEventReader createEventReader(InputStream configStream) throws ParsingException { if (configStream == null) throw logger.nullArgumentError("InputStream"); XMLEventReader xmlEventReader = StaxParserUtil.getXMLEventReader(configStream); - try { - xmlEventReader = filterWhitespaces(xmlEventReader); - } catch (XMLStreamException e) { - throw logger.parserException(e); - } - - return xmlEventReader; + return filterWhitespaces(xmlEventReader); } public XMLEventReader createEventReader(Source source) throws ParsingException { @@ -120,36 +117,39 @@ public abstract class AbstractParser implements ParserNamespaceSupport { XMLEventReader xmlEventReader = StaxParserUtil.getXMLEventReader(source); - try { - xmlEventReader = filterWhitespaces(xmlEventReader); - } catch (XMLStreamException e) { - throw logger.parserException(e); - } - - return xmlEventReader; + return filterWhitespaces(xmlEventReader); } - protected XMLEventReader filterWhitespaces(XMLEventReader xmlEventReader) throws XMLStreamException { + private static final Pattern WHITESPACE_ONLY = Pattern.compile("\\s*"); + + /** + * Creates a derived {@link XMLEventReader} that ignores all events except for: {@link StartElement}, + * {@link EndElement}, and non-empty and non-whitespace-only {@link Characters}. + * + * @param xmlEventReader Original {@link XMLEventReader} + * @return Derived {@link XMLEventReader} + * @throws XMLStreamException + */ + private static XMLEventReader filterWhitespaces(XMLEventReader xmlEventReader) throws ParsingException { XMLInputFactory xmlInputFactory = XML_INPUT_FACTORY.get(); - xmlEventReader = xmlInputFactory.createFilteredReader(xmlEventReader, new EventFilter() { - public boolean accept(XMLEvent xmlEvent) { - // We are going to disregard characters that are new line and whitespace - if (xmlEvent.isCharacters()) { - Characters chars = xmlEvent.asCharacters(); - String data = chars.getData(); - data = valid(data) ? data.trim() : null; - return valid(data); - } else { - return xmlEvent.isStartElement() || xmlEvent.isEndElement(); + try { + xmlEventReader = xmlInputFactory.createFilteredReader(xmlEventReader, new EventFilter() { + @Override + public boolean accept(XMLEvent xmlEvent) { + // We are going to disregard characters that are new line and whitespace + if (xmlEvent.isCharacters()) { + Characters chars = xmlEvent.asCharacters(); + String data = chars.getData(); + return data != null && ! WHITESPACE_ONLY.matcher(data).matches(); + } else { + return xmlEvent.isStartElement() || xmlEvent.isEndElement(); + } } - } - - private boolean valid(String str) { - return str != null && ! str.isEmpty(); - } - - }); + }); + } catch (XMLStreamException ex) { + throw logger.parserException(ex); + } // Handle IBM JDK bug with Stax parsing when EventReader presented if (Environment.IS_IBM_JAVA) { diff --git a/saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractStaxParser.java b/saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractStaxParser.java new file mode 100644 index 0000000000..5d1dbdd460 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/common/parsers/AbstractStaxParser.java @@ -0,0 +1,151 @@ +/* + * 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.common.parsers; + +import org.keycloak.saml.common.PicketLinkLogger; +import org.keycloak.saml.common.PicketLinkLoggerFactory; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import java.util.Objects; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +/** + * Simple support for STaX type of parsing. Parses single element and allows processing its direct children. + * + * @param Java class that will be result of parsing this element + * @param Type containing all tokens that can be found in subelements of the element parsed by this parser, usually an enum + * @author hmlnarik + */ +public abstract class AbstractStaxParser implements StaxParser { + + protected static final PicketLinkLogger LOGGER = PicketLinkLoggerFactory.getLogger(); + private final QName expectedStartElement; + private final E unknownElement; + + public AbstractStaxParser(QName expectedStartElement, E unknownElement) { + this.unknownElement = unknownElement; + this.expectedStartElement = expectedStartElement; + } + + @Override + public T parse(XMLEventReader xmlEventReader) throws ParsingException { + // STATE: should be before the expected start element + + // Get the start element and validate it is the expected one + StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); + StaxParserUtil.validate(startElement, expectedStartElement); + T target = instantiateElement(xmlEventReader, startElement); + + // STATE: Start element has been read. + QName currentSubelement = null; + + while (xmlEventReader.hasNext()) { + // STATE: the only end element that can be found at this phase must correspond to the expected start element + XMLEvent xmlEvent = StaxParserUtil.peekNextTag(xmlEventReader); + if (xmlEvent == null) { + break; + } + + if (xmlEvent instanceof EndElement) { + EndElement endElement = (EndElement) xmlEvent; + final QName qName = endElement.getName(); + + // If leftover from processed subelement, just consume. + if (Objects.equals(qName, currentSubelement)) { + StaxParserUtil.advance(xmlEventReader); + currentSubelement = null; + continue; + } + + // If end element corresponding to this start element, stop processing. + if (Objects.equals(qName, expectedStartElement)) { + // consume the end element and finish parsing of this tag + StaxParserUtil.advance(xmlEventReader); + break; + } + + // No other case is valid + String elementName = StaxParserUtil.getElementName(endElement); + throw LOGGER.parserUnknownEndElement(elementName, xmlEvent.getLocation()); + } + + startElement = (StartElement) xmlEvent; + currentSubelement = startElement.getName(); + E token = getElementFromName(currentSubelement); + if (token == null) { + token = unknownElement; + } + processSubElement(xmlEventReader, target, token, startElement); + + // If the XMLEventReader has not advanced inside processSubElement (hence using "==" and not "equals"), advance it. + if (StaxParserUtil.peek(xmlEventReader) == startElement) { + StaxParserUtil.bypassElementBlock(xmlEventReader); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("Element %s bypassed", currentSubelement)); + } + } + + // In case of recursive nesting the same element, the corresponding end element MUST be handled + // in the {@code processSubElement} method and MUST NOT be consumed here. + if (Objects.equals(expectedStartElement, currentSubelement) || isUnknownElement(token)) { + currentSubelement = null; + } + } + return target; + } + + + protected boolean isUnknownElement(E token) { + return token == null || Objects.equals(token, unknownElement); + } + + protected abstract E getElementFromName(QName name); + + /** + * Instantiates the target Java class representing the current element.
+ * Precondition: Current event is the {@link StartElement}
+ * Postcondition: Current event is the {@link StartElement} or the {@link EndElement} corresponding to the {@link StartElement} + * @param xmlEventReader + * @param element The XML event that was just read from the {@code xmlEventReader} + * @return + * @throws ParsingException + */ + protected abstract T instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException; + + /** + * Processes the subelement of the element processed in {@link #instantiateElement} method.
+ * Precondition: Current event: Last before the {@link StartElement} corresponding to the processed subelement, i.e. + * event obtained by {@link XMLEventReader#next()} is the {@link StartElement} of the subelement being processed
+ * Postcondition: Event obtained by {@link XMLEventReader#next()} is either + * the same {@link StartElement} (i.e. no change in position which causes this subelement to be skipped), + * the corresponding {@link EndElement}, or the event after the corresponding {@link EndElement}. + *

+ * Note that in case of recursive nesting the same element, the corresponding end element MUST be consumed in this method. + * @param xmlEventReader + * @param target Target object (the one created by the {@link #instantiateElement} method. + * @param element The constant corresponding to the current start element. + * @param elementDetail The XML event that was just read from the {@code xmlEventReader} + * @return + * @throws ParsingException + */ + protected abstract void processSubElement(XMLEventReader xmlEventReader, T target, E element, StartElement elementDetail) throws ParsingException; + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/common/parsers/AnyDomParser.java b/saml-core/src/main/java/org/keycloak/saml/common/parsers/AnyDomParser.java new file mode 100644 index 0000000000..e64fd8b427 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/common/parsers/AnyDomParser.java @@ -0,0 +1,64 @@ +/* + * 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.common.parsers; + +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.w3c.dom.Element; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.util.LinkedList; +import java.util.List; +import javax.xml.namespace.QName; + +/** + * Parses any DOM tree to a list of DOM representations. + */ +public class AnyDomParser extends AbstractStaxParser, AnyDomParser.Dom> { + + public static enum Dom { ANY_DOM }; + + public AnyDomParser(QName name) { + super(name, Dom.ANY_DOM); + } + + public static AnyDomParser getInstance(QName name) { + return new AnyDomParser(name); + } + + @Override + protected List instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new LinkedList<>(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, List target, Dom element, StartElement elementDetail) throws ParsingException { + target.add(StaxParserUtil.getDOMElement(xmlEventReader)); + } + + @Override + protected boolean isUnknownElement(Dom token) { + return true; + } + + @Override + protected Dom getElementFromName(QName name) { + return Dom.ANY_DOM; + } + +} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/common/parsers/ParserNamespaceSupport.java b/saml-core/src/main/java/org/keycloak/saml/common/parsers/StaxParser.java similarity index 68% rename from saml-core/src/main/java/org/keycloak/saml/common/parsers/ParserNamespaceSupport.java rename to saml-core/src/main/java/org/keycloak/saml/common/parsers/StaxParser.java index 0011cedced..8f78a79282 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/parsers/ParserNamespaceSupport.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/parsers/StaxParser.java @@ -18,22 +18,12 @@ package org.keycloak.saml.common.parsers; import org.keycloak.saml.common.exceptions.ParsingException; -import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; /** - *

- * Interface to indicate the parser supports a particular namespace. - *

- * - *

- * This class needs to be moved to the security common project. - *

- * - * @author Anil.Saldhana@redhat.com - * @since Feb 5, 2010 + * Interface to indicate the parser. */ -public interface ParserNamespaceSupport { +public interface StaxParser { /** * Parse the event stream * @@ -42,12 +32,4 @@ public interface ParserNamespaceSupport { * @throws ParsingException */ Object parse(XMLEventReader xmlEventReader) throws ParsingException; - - /** - * Returns whether the parser supports parsing a particular namespace - * - * @param qname - * @return - */ - boolean supports(QName qname); } \ No newline at end of file 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 8cba8eba40..1fc338cea3 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 @@ -20,12 +20,13 @@ import org.keycloak.saml.common.ErrorCodes; 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.ConfigurationException; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.saml.processing.core.parsers.util.HasQName; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -49,8 +50,10 @@ import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import java.io.InputStream; +import java.net.URI; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import javax.xml.datatype.XMLGregorianCalendar; /** * Utility for the stax based parser @@ -103,8 +106,12 @@ public class StaxParserUtil { * * @throws org.keycloak.saml.common.exceptions.ParsingException */ - public static void bypassElementBlock(XMLEventReader xmlEventReader, JBossSAMLConstants tag) throws ParsingException { - bypassElementBlock(xmlEventReader, tag == null ? null : tag.get()); + public static void bypassElementBlock(XMLEventReader xmlEventReader, QName tag) throws ParsingException { + XMLEvent xmlEvent = bypassElementBlock(xmlEventReader); + + if (! (xmlEvent instanceof EndElement) || ! Objects.equals(((EndElement) xmlEvent).getName(), tag)) { + throw logger.parserExpectedEndTag(tag.getLocalPart()); + } } /** @@ -173,8 +180,13 @@ public class StaxParserUtil { * @return */ public static String getAttributeValue(Attribute attribute) { - String str = trim(attribute.getValue()); - return str; + if (attribute == null) { + return null; + } + + final String value = attribute.getValue(); + + return value == null ? null : trim(value); } /** @@ -185,12 +197,90 @@ public class StaxParserUtil { * * @return */ + @Deprecated public static String getAttributeValue(StartElement startElement, String tag) { - String result = null; - Attribute attr = startElement.getAttributeByName(new QName(tag)); - if (attr != null) - result = getAttributeValue(attr); - return result; + return getAttributeValue(startElement, new QName(tag)); + } + + /** + * Get the Attribute value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static String getAttributeValue(StartElement startElement, HasQName attrName) { + return getAttributeValue(startElement, attrName.getQName()); + } + + /** + * Get the Attribute value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static String getAttributeValue(StartElement startElement, QName attrQName) { + Attribute attr = startElement.getAttributeByName(attrQName); + return getAttributeValue(attr); + } + + /** + * Get the Attribute value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static URI getUriAttributeValue(StartElement startElement, HasQName attrName) { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValue(attr); + return value == null ? null : URI.create(value); + } + + /** + * Get the Attribute value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static XMLGregorianCalendar getXmlTimeAttributeValue(StartElement startElement, HasQName attrName) throws ParsingException { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValue(attr); + 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 Integer getIntegerAttributeValue(StartElement startElement, HasQName attrName) { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValue(attr); + return value == null ? null : Integer.valueOf(value); + } + + /** + * Get the Attribute value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static Boolean getBooleanAttributeValue(StartElement startElement, HasQName attrName) { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValue(attr); + return value == null ? null : Boolean.valueOf(value); } /** @@ -201,6 +291,7 @@ public class StaxParserUtil { * * @return false if attribute not set */ + @Deprecated public static boolean getBooleanAttributeValue(StartElement startElement, String tag) { return getBooleanAttributeValue(startElement, tag, false); } @@ -213,6 +304,7 @@ public class StaxParserUtil { * * @return false if attribute not set */ + @Deprecated public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) { String result = null; Attribute attr = startElement.getAttributeByName(new QName(tag)); @@ -222,6 +314,16 @@ public class StaxParserUtil { return Boolean.valueOf(result); } + public static String getRequiredAttributeValue(StartElement startElement, HasQName attrName) throws ParsingException { + final QName qName = attrName.getQName(); + Attribute attr = startElement.getAttributeByName(qName); + if (attr == null) + throw logger.parserRequiredAttribute(qName.getLocalPart()); + return StaxParserUtil.getAttributeValue(attr); + } + + private static final String JDK_TRANSFORMER_PROPERTY = "picketlink.jdk.transformer"; + /** * Given that the {@code XMLEventReader} is in {@code XMLStreamConstants.START_ELEMENT} mode, we parse into a DOM * Element @@ -233,9 +335,7 @@ public class StaxParserUtil { * @throws ParsingException */ public static Element getDOMElement(XMLEventReader xmlEventReader) throws ParsingException { - Transformer transformer = null; - - final String JDK_TRANSFORMER_PROPERTY = "picketlink.jdk.transformer"; + Transformer transformer; boolean useJDKTransformer = Boolean.parseBoolean(SecurityActions.getSystemProperty(JDK_TRANSFORMER_PROPERTY, "false")); @@ -263,7 +363,9 @@ public class StaxParserUtil { } /** - * Get the element text. + * Get the element text. Following {@link XMLEventReader#getElementText()}: + * Precondition: the current event is START_ELEMENT. + * Postcondition: The current event is the corresponding END_ELEMENT. * * @param xmlEventReader * @@ -411,7 +513,7 @@ public class StaxParserUtil { * * @return */ - public static String getStartElementName(StartElement startElement) { + public static String getElementName(StartElement startElement) { return trim(startElement.getName().getLocalPart()); } @@ -422,10 +524,12 @@ public class StaxParserUtil { * * @return */ - public static String getEndElementName(EndElement endElement) { + public static String getElementName(EndElement endElement) { return trim(endElement.getName().getLocalPart()); } + private static final QName XSI_TYPE = new QName(JBossSAMLURIConstants.XSI_NSURI.get(), "type", JBossSAMLURIConstants.XSI_PREFIX.get()); + /** * Given a start element, obtain the xsi:type defined * @@ -436,8 +540,7 @@ public class StaxParserUtil { * @throws RuntimeException if xsi:type is missing */ public static String getXSITypeValue(StartElement startElement) { - Attribute xsiType = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XSI_NSURI.get(), - JBossSAMLConstants.TYPE.get())); + Attribute xsiType = startElement.getAttributeByName(XSI_TYPE); if (xsiType == null) throw logger.parserExpectedXSI(ErrorCodes.EXPECTED_XSI); return StaxParserUtil.getAttributeValue(xsiType); @@ -466,7 +569,7 @@ public class StaxParserUtil { * @return boolean if the tags match */ public static boolean matches(StartElement startElement, String tag) { - String elementTag = getStartElementName(startElement); + String elementTag = StaxParserUtil.getElementName(startElement); return tag.equals(elementTag); } @@ -479,7 +582,7 @@ public class StaxParserUtil { * @return boolean if the tags match */ public static boolean matches(EndElement endElement, String tag) { - String elementTag = getEndElementName(endElement); + String elementTag = getElementName(endElement); return tag.equals(elementTag); } @@ -500,6 +603,23 @@ public class StaxParserUtil { } } + /** + * Consume the next event + * + * @param xmlEventReader + * + * @return + * + * @throws ParsingException + */ + public static XMLEvent advance(XMLEventReader xmlEventReader) throws ParsingException { + try { + return xmlEventReader.nextEvent(); + } catch (XMLStreamException e) { + throw logger.parserException(e); + } + } + /** * Peek the next {@code StartElement } * @@ -511,14 +631,32 @@ public class StaxParserUtil { */ public static StartElement peekNextStartElement(XMLEventReader xmlEventReader) throws ParsingException { try { - while (true) { - XMLEvent xmlEvent = xmlEventReader.peek(); - - if (xmlEvent == null || xmlEvent.isStartElement()) - return (StartElement) xmlEvent; - else - xmlEvent = xmlEventReader.nextEvent(); + XMLEvent xmlEvent = xmlEventReader.peek(); + while (xmlEvent != null && ! xmlEvent.isStartElement()) { + xmlEventReader.nextEvent(); + xmlEvent = xmlEventReader.peek(); } + return (StartElement) xmlEvent; + } catch (XMLStreamException e) { + throw logger.parserException(e); + } + } + + /** + * Peek the next {@link StartElement} or {@link EndElement}. + * + * @param xmlEventReader + * @return + * @throws ParsingException + */ + public static XMLEvent peekNextTag(XMLEventReader xmlEventReader) throws ParsingException { + try { + XMLEvent xmlEvent = xmlEventReader.peek(); + while (xmlEvent != null && ! xmlEvent.isStartElement() && ! xmlEvent.isEndElement()) { + xmlEventReader.nextEvent(); + xmlEvent = xmlEventReader.peek(); + } + return xmlEvent; } catch (XMLStreamException e) { throw logger.parserException(e); } @@ -571,12 +709,28 @@ public class StaxParserUtil { * * @throws RuntimeException mismatch */ + @Deprecated public static void validate(StartElement startElement, String tag) { - String foundElementTag = getStartElementName(startElement); + String foundElementTag = StaxParserUtil.getElementName(startElement); if (!tag.equals(foundElementTag)) throw logger.parserExpectedTag(tag, foundElementTag); } + /** + * Validate that the start element has the expected tag + * + * @param startElement + * @param tag + * + * @throws RuntimeException mismatch + */ + public static void validate(StartElement startElement, QName tag) { + if (! Objects.equals(startElement.getName(), tag)) { + String foundElementTag = StaxParserUtil.getElementName(startElement); + throw logger.parserExpectedTag(tag.getLocalPart(), foundElementTag); + } + } + /** * Validate that the end element has the expected tag * @@ -586,7 +740,7 @@ public class StaxParserUtil { * @throws RuntimeException mismatch */ public static void validate(EndElement endElement, String tag) { - String elementTag = getEndElementName(endElement); + String elementTag = getElementName(endElement); if (!tag.equals(elementTag)) throw new RuntimeException(logger.parserExpectedEndTag(". Found ")); } diff --git a/saml-core/src/main/java/org/keycloak/saml/common/util/TransformerUtil.java b/saml-core/src/main/java/org/keycloak/saml/common/util/TransformerUtil.java index 4e43c3d333..14c3fe012e 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/util/TransformerUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/util/TransformerUtil.java @@ -227,20 +227,26 @@ public class TransformerUtil { throw new TransformerException(ErrorCodes.WRITER_SHOULD_START_ELEMENT); StartElement rootElement = (StartElement) xmlEvent; - rootTag = StaxParserUtil.getStartElementName(rootElement); - Element docRoot = handleStartElement(xmlEventReader, rootElement, new CustomHolder(doc, false)); + rootTag = StaxParserUtil.getElementName(rootElement); + CustomHolder holder = new CustomHolder(doc, false); + Element docRoot = handleStartElement(xmlEventReader, rootElement, holder); Node parent = doc.importNode(docRoot, true); doc.appendChild(parent); stack.push(parent); + if (holder.encounteredTextNode) { + // Handling text node skips over the corresponding end element, see {@link XMLEventReader#getElementText()} + return; + } + while (xmlEventReader.hasNext()) { xmlEvent = StaxParserUtil.getNextEvent(xmlEventReader); int type = xmlEvent.getEventType(); switch (type) { case XMLEvent.START_ELEMENT: StartElement startElement = (StartElement) xmlEvent; - CustomHolder holder = new CustomHolder(doc, false); + holder = new CustomHolder(doc, false); Element docStartElement = handleStartElement(xmlEventReader, startElement, holder); Node el = doc.importNode(docStartElement, true); @@ -261,7 +267,7 @@ public class TransformerUtil { break; case XMLEvent.END_ELEMENT: EndElement endElement = (EndElement) xmlEvent; - String endTag = StaxParserUtil.getEndElementName(endElement); + String endTag = StaxParserUtil.getElementName(endElement); if (rootTag.equals(endTag)) return; // We are done with the dom parsing else { @@ -334,7 +340,7 @@ public class TransformerUtil { String prefix = elementName.getPrefix(); String localPart = elementName.getLocalPart(); - String qual = prefix != null && prefix != "" ? prefix + ":" + localPart : localPart; + String qual = (prefix != null && ! prefix.isEmpty()) ? prefix + ":" + localPart : localPart; Element el = doc.createElementNS(ns, qual); @@ -356,7 +362,7 @@ public class TransformerUtil { ns = attrName.getNamespaceURI(); prefix = attrName.getPrefix(); localPart = attrName.getLocalPart(); - qual = prefix != null && prefix != "" ? prefix + ":" + localPart : localPart; + qual = (prefix != null && ! prefix.isEmpty()) ? prefix + ":" + localPart : localPart; if (logger.isTraceEnabled()) { logger.trace("Creating an Attribute Namespace=" + ns + ":" + qual); @@ -373,8 +379,8 @@ public class TransformerUtil { QName name = namespace.getName(); localPart = name.getLocalPart(); prefix = name.getPrefix(); - if (prefix != null && prefix != "") - qual = (localPart != null && localPart != "") ? prefix + ":" + localPart : prefix; + if (prefix != null && ! prefix.isEmpty()) + qual = (localPart != null && ! localPart.isEmpty()) ? prefix + ":" + localPart : prefix; if (qual.equals("xmlns")) continue; @@ -423,8 +429,8 @@ public class TransformerUtil { QName name = namespace.getName(); localPart = name.getLocalPart(); prefix = name.getPrefix(); - if (prefix != null && prefix != "") - qual = (localPart != null && localPart != "") ? prefix + ":" + localPart : prefix; + if (prefix != null && ! prefix.isEmpty()) + qual = (localPart != null && ! localPart.isEmpty()) ? prefix + ":" + localPart : prefix; if (qual != null && qual.equals("xmlns")) return namespace.getNamespaceURI(); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/request/SAML2Request.java b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/request/SAML2Request.java index cb8a348631..5a131d28d3 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/request/SAML2Request.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/request/SAML2Request.java @@ -26,7 +26,6 @@ import org.keycloak.dom.saml.v2.protocol.ResponseType; 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.ConfigurationException; import org.keycloak.saml.common.exceptions.ParsingException; @@ -94,7 +93,7 @@ public class SAML2Request { AuthnRequestType authnRequest = new AuthnRequestType(id, issueInstant); authnRequest.setAssertionConsumerServiceURL(URI.create(assertionConsumerURL)); - authnRequest.setProtocolBinding(URI.create(JBossSAMLConstants.HTTP_POST_BINDING.get())); + authnRequest.setProtocolBinding(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri()); if (destination != null) { authnRequest.setDestination(URI.create(destination)); } @@ -163,7 +162,7 @@ public class SAML2Request { Document samlDocument = DocumentUtil.getDocument(is); - SAMLParser samlParser = new SAMLParser(); + SAMLParser samlParser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(samlDocument); SAML2Object requestType = (SAML2Object) samlParser.parse(samlDocument); @@ -189,7 +188,7 @@ public class SAML2Request { Document samlDocument = DocumentUtil.getDocument(is); - SAMLParser samlParser = new SAMLParser(); + SAMLParser samlParser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(samlDocument); RequestAbstractType requestType = (RequestAbstractType) samlParser.parse(samlDocument); @@ -216,7 +215,7 @@ public class SAML2Request { Document samlDocument = DocumentUtil.getDocument(is); - SAMLParser samlParser = new SAMLParser(); + SAMLParser samlParser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(samlDocument); AuthnRequestType requestType = (AuthnRequestType) samlParser.parse(samlDocument); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/response/SAML2Response.java b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/response/SAML2Response.java index a650c7a25f..e9a86b15cf 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/response/SAML2Response.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/response/SAML2Response.java @@ -85,9 +85,9 @@ import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_ public class SAML2Response { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - private long ASSERTION_VALIDITY = 5000; // 5secs in milis + private final long ASSERTION_VALIDITY = 5000; // 5secs in milis - private long CLOCK_SKEW = 2000; // 2secs + private final long CLOCK_SKEW = 2000; // 2secs private SAMLDocumentHolder samlDocumentHolder = null; @@ -118,7 +118,7 @@ public class SAML2Response { act.addAuthenticatingAuthority(URI.create(authContextDeclRef)); AuthnContextType.AuthnContextTypeSequence sequence = act.new AuthnContextTypeSequence(); - sequence.setClassRef(new AuthnContextClassRefType(URI.create(JBossSAMLURIConstants.AC_PASSWORD.get()))); + sequence.setClassRef(new AuthnContextClassRefType(JBossSAMLURIConstants.AC_PASSWORD.getUri())); act.setSequence(sequence); authnStatement.setAuthnContext(act); @@ -264,7 +264,7 @@ public class SAML2Response { subjectType.addConfirmation(subjectConfirmation); - AssertionType assertionType = null; + AssertionType assertionType; NameIDType issuerID = issuerInfo.getIssuer(); try { issueInstant = XMLTimeUtil.getIssueInstant(); @@ -373,7 +373,7 @@ public class SAML2Response { throw logger.nullArgumentError("InputStream"); Document samlDocument = DocumentUtil.getDocument(is); - SAMLParser samlParser = new SAMLParser(); + SAMLParser samlParser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(samlDocument); return (EncryptedAssertionType) samlParser.parse(samlDocument); @@ -396,7 +396,7 @@ public class SAML2Response { throw logger.nullArgumentError("InputStream"); Document samlDocument = DocumentUtil.getDocument(is); - SAMLParser samlParser = new SAMLParser(); + SAMLParser samlParser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(samlDocument); return (AssertionType) samlParser.parse(samlDocument); } @@ -426,7 +426,7 @@ public class SAML2Response { Document samlResponseDocument = DocumentUtil.getDocument(is); - SAMLParser samlParser = new SAMLParser(); + SAMLParser samlParser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(samlResponseDocument); ResponseType responseType = (ResponseType) samlParser.parse(samlResponseDocument); @@ -457,7 +457,7 @@ public class SAML2Response { logger.trace("SAML Response Document: " + DocumentUtil.asString(samlResponseDocument)); } - SAMLParser samlParser = new SAMLParser(); + SAMLParser samlParser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(samlResponseDocument); SAML2Object responseType = (SAML2Object) samlParser.parse(samlResponseDocument); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11AssertionParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11AssertionParser.java index d239b0fb83..f5ccb9d425 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11AssertionParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11AssertionParser.java @@ -30,7 +30,6 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ProcessingException; -import org.keycloak.saml.common.parsers.ParserNamespaceSupport; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.common.util.StringUtil; @@ -46,6 +45,7 @@ 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 assertion @@ -53,7 +53,7 @@ import javax.xml.stream.events.XMLEvent; * @author Anil.Saldhana@redhat.com * @since Oct 12, 2010 */ -public class SAML11AssertionParser implements ParserNamespaceSupport { +public class SAML11AssertionParser implements StaxParser { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); @@ -90,11 +90,11 @@ public class SAML11AssertionParser implements ParserNamespaceSupport { if (xmlEvent instanceof EndElement) { xmlEvent = StaxParserUtil.getNextEvent(xmlEventReader); EndElement endElement = (EndElement) xmlEvent; - String endElementTag = StaxParserUtil.getEndElementName(endElement); + String endElementTag = StaxParserUtil.getElementName(endElement); if (endElementTag.equals(JBossSAMLConstants.ASSERTION.get())) break; else - throw logger.parserUnknownEndElement(endElementTag); + throw logger.parserUnknownEndElement(endElementTag, xmlEvent.getLocation()); } StartElement peekedElement = null; @@ -107,7 +107,7 @@ public class SAML11AssertionParser implements ParserNamespaceSupport { if (peekedElement == null) break; - String tag = StaxParserUtil.getStartElementName(peekedElement); + String tag = StaxParserUtil.getElementName(peekedElement); if (tag.equals(JBossSAMLConstants.SIGNATURE.get())) { assertion.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); @@ -144,16 +144,6 @@ public class SAML11AssertionParser implements ParserNamespaceSupport { return assertion; } - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.ASSERTION_NSURI.get()) - && localPart.equals(JBossSAMLConstants.ASSERTION.get()); - } private SAML11AssertionType parseBaseAttributes(StartElement nextElement) throws ParsingException { Attribute idAttribute = nextElement.getAttributeByName(new QName(SAML11Constants.ASSERTIONID)); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11RequestParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11RequestParser.java index eff7e78e6c..92ecd22b85 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11RequestParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11RequestParser.java @@ -25,7 +25,6 @@ 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.ParserNamespaceSupport; import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.processing.core.parsers.util.SAML11ParserUtil; import org.keycloak.saml.processing.core.saml.v1.SAML11Constants; @@ -36,6 +35,7 @@ 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.common.parsers.StaxParser; /** * Parse the SAML2 AuthnRequest @@ -43,7 +43,7 @@ import javax.xml.stream.events.StartElement; * @author Anil.Saldhana@redhat.com * @since June 24, 2011 */ -public class SAML11RequestParser implements ParserNamespaceSupport { +public class SAML11RequestParser implements StaxParser { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); @@ -77,7 +77,7 @@ public class SAML11RequestParser implements ParserNamespaceSupport { if (startElement == null) break; - String elementName = StaxParserUtil.getStartElementName(startElement); + String elementName = StaxParserUtil.getElementName(startElement); if (SAML11Constants.ATTRIBUTE_QUERY.equals(elementName)) { startElement = StaxParserUtil.getNextStartElement(xmlEventReader); @@ -106,10 +106,4 @@ public class SAML11RequestParser implements ParserNamespaceSupport { return request; } - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()); - } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11ResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11ResponseParser.java index b75fb3082c..eea572b7de 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11ResponseParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11ResponseParser.java @@ -25,7 +25,6 @@ import org.keycloak.saml.common.PicketLinkLogger; import org.keycloak.saml.common.PicketLinkLoggerFactory; import org.keycloak.saml.common.constants.JBossSAMLConstants; import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.ParserNamespaceSupport; import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.processing.core.saml.v1.SAML11Constants; import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; @@ -38,6 +37,7 @@ 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 11 Response @@ -45,11 +45,11 @@ import javax.xml.stream.events.XMLEvent; * @author Anil.Saldhana@redhat.com * @since 23 June 2011 */ -public class SAML11ResponseParser implements ParserNamespaceSupport { +public class SAML11ResponseParser implements StaxParser { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - private final String RESPONSE = JBossSAMLConstants.RESPONSE.get(); + private final String RESPONSE = JBossSAMLConstants.RESPONSE__PROTOCOL.get(); /** * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} @@ -76,7 +76,7 @@ public class SAML11ResponseParser implements ParserNamespaceSupport { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String elementName = StaxParserUtil.getStartElementName(startElement); + String elementName = StaxParserUtil.getElementName(startElement); if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { Element sig = StaxParserUtil.getDOMElement(xmlEventReader); response.setSignature(sig); @@ -172,17 +172,11 @@ public class SAML11ResponseParser implements ParserNamespaceSupport { if (StaxParserUtil.matches(endElement, STATUS)) break; else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); + throw logger.parserUnknownEndElement(StaxParserUtil.getElementName(endElement), xmlEvent.getLocation()); } else break; } return status; } - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return SAML11Constants.PROTOCOL_11_NSURI.equals(qname.getNamespaceURI()) && RESPONSE.equals(qname.getLocalPart()); - } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11SubjectParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11SubjectParser.java index fbfd58e57d..7839262631 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11SubjectParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAML11SubjectParser.java @@ -25,7 +25,6 @@ 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.ParserNamespaceSupport; import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.processing.core.parsers.util.SAML11ParserUtil; import org.keycloak.saml.processing.core.saml.v1.SAML11Constants; @@ -37,6 +36,7 @@ import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import java.net.URI; +import org.keycloak.saml.common.parsers.StaxParser; /** * Parse the saml subject @@ -44,7 +44,7 @@ import java.net.URI; * @author Anil.Saldhana@redhat.com * @since Oct 12, 2010 */ -public class SAML11SubjectParser implements ParserNamespaceSupport { +public class SAML11SubjectParser implements StaxParser { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); @@ -65,14 +65,14 @@ public class SAML11SubjectParser implements ParserNamespaceSupport { endElement = StaxParserUtil.getNextEndElement(xmlEventReader); break; } else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); + throw logger.parserUnknownEndElement(StaxParserUtil.getElementName(endElement), xmlEvent.getLocation()); } StartElement peekedElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (peekedElement == null) break; - String tag = StaxParserUtil.getStartElementName(peekedElement); + String tag = StaxParserUtil.getElementName(peekedElement); if (SAML11Constants.NAME_IDENTIFIER.equalsIgnoreCase(tag)) { peekedElement = StaxParserUtil.getNextStartElement(xmlEventReader); @@ -100,14 +100,5 @@ public class SAML11SubjectParser implements ParserNamespaceSupport { return subject; } - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.ASSERTION_NSURI.get()) && localPart.equals(JBossSAMLConstants.SUBJECT.get()); - } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java deleted file mode 100755 index 06d6042fde..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.protocol.ArtifactResolveType; -import org.keycloak.saml.common.ErrorCodes; -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.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.StartElement; - -/** - * Parse the {@link ArtifactResolveType} - * - * @author Anil.Saldhana@redhat.com - * @since Jul 1, 2011 - */ -public class SAMLArtifactResolveParser extends SAMLRequestAbstractParser implements ParserNamespaceSupport { - - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ARTIFACT_RESOLVE.get()); - - ArtifactResolveType artifactResolve = parseBaseAttributes(startElement); - - while (xmlEventReader.hasNext()) { - // Let us peek at the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - super.parseCommonElements(startElement, xmlEventReader, artifactResolve); - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.ARTIFACT.get().equals(elementName)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - artifactResolve.setArtifact(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - continue; - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" - + startElement.getLocation()); - } - return artifactResolve; - } - - public boolean supports(QName qname) { - return JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()); - } - - /** - * Parse the attributes at the authnrequesttype element - * - * @param startElement - * - * @return - * - * @throws ParsingException - */ - private ArtifactResolveType parseBaseAttributes(StartElement startElement) throws ParsingException { - super.parseRequiredAttributes(startElement); - ArtifactResolveType authnRequest = new ArtifactResolveType(id, issueInstant); - // Let us get the attributes - super.parseBaseAttributes(startElement, authnRequest); - - return authnRequest; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java deleted file mode 100755 index 9d3686bb17..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.protocol.ArtifactResponseType; -import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; -import org.keycloak.dom.saml.v2.protocol.ResponseType; -import org.keycloak.dom.saml.v2.protocol.StatusResponseType; -import org.keycloak.saml.common.ErrorCodes; -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.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.StartElement; - -/** - * Parse the SAML Response - * - * @author Anil.Saldhana@redhat.com - * @since July 1, 2011 - */ -public class SAMLArtifactResponseParser extends SAMLStatusResponseTypeParser implements ParserNamespaceSupport { - - private final String ARTIFACT_RESPONSE = JBossSAMLConstants.ARTIFACT_RESPONSE.get(); - - /** - * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} - */ - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ARTIFACT_RESPONSE); - - ArtifactResponseType response = (ArtifactResponseType) parseBaseAttributes(startElement); - - while (xmlEventReader.hasNext()) { - // Let us peek at the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - NameIDType issuer = new NameIDType(); - issuer.setValue(StaxParserUtil.getElementText(xmlEventReader)); - response.setIssuer(issuer); - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - Element sig = StaxParserUtil.getDOMElement(xmlEventReader); - response.setSignature(sig); - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser(); - response.setExtensions(extensionsParser.parse(xmlEventReader)); - } else if (JBossSAMLConstants.AUTHN_REQUEST.get().equals(elementName)) { - SAMLAuthNRequestParser authnParser = new SAMLAuthNRequestParser(); - AuthnRequestType authn = (AuthnRequestType) authnParser.parse(xmlEventReader); - response.setAny(authn); - } else if (JBossSAMLConstants.RESPONSE.get().equals(elementName)) { - SAMLResponseParser authnParser = new SAMLResponseParser(); - ResponseType authn = (ResponseType) authnParser.parse(xmlEventReader); - response.setAny(authn); - } else if (JBossSAMLConstants.STATUS.get().equals(elementName)) { - response.setStatus(parseStatus(xmlEventReader)); - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" - + startElement.getLocation()); - } - - return response; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()) - && ARTIFACT_RESPONSE.equals(qname.getLocalPart()); - } - - /** - * Parse the attributes at the response element - * - * @param startElement - * - * @return - * - * @throws ParsingException - */ - protected StatusResponseType parseBaseAttributes(StartElement startElement) throws ParsingException { - ArtifactResponseType response = new ArtifactResponseType(super.parseBaseAttributes(startElement)); - return response; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAssertionParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAssertionParser.java deleted file mode 100755 index 057a2b7c31..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAssertionParser.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.AssertionType; -import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; -import org.keycloak.dom.saml.v2.assertion.AuthnStatementType; -import org.keycloak.dom.saml.v2.assertion.ConditionsType; -import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType; -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.assertion.SubjectType; -import org.keycloak.saml.common.ErrorCodes; -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.ConfigurationException; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.exceptions.ProcessingException; -import org.keycloak.saml.common.parsers.ParserNamespaceSupport; -import org.keycloak.saml.common.util.DocumentUtil; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; -import org.w3c.dom.Element; - -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; - -/** - * Parse the saml assertion - * - * @author Anil.Saldhana@redhat.com - * @since Oct 12, 2010 - */ -public class SAMLAssertionParser implements ParserNamespaceSupport { - private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - - private final String ASSERTION = JBossSAMLConstants.ASSERTION.get(); - - public AssertionType fromElement(Element element) throws ConfigurationException, ProcessingException, ParsingException { - XMLEventReader xmlEventReader = StaxParserUtil.getXMLEventReader(DocumentUtil.getNodeAsStream(element)); - return (AssertionType) parse(xmlEventReader); - } - - /** - * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} - */ - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - String startElementName = StaxParserUtil.getStartElementName(startElement); - if (startElementName.equals(JBossSAMLConstants.ENCRYPTED_ASSERTION.get())) { - Element domElement = StaxParserUtil.getDOMElement(xmlEventReader); - - EncryptedAssertionType encryptedAssertion = new EncryptedAssertionType(); - encryptedAssertion.setEncryptedElement(domElement); - return encryptedAssertion; - } - - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - - // Special case: Encrypted Assertion - StaxParserUtil.validate(startElement, ASSERTION); - AssertionType assertion = parseBaseAttributes(startElement); - - // Peek at the next event - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - - if (xmlEvent instanceof EndElement) { - xmlEvent = StaxParserUtil.getNextEvent(xmlEventReader); - EndElement endElement = (EndElement) xmlEvent; - String endElementTag = StaxParserUtil.getEndElementName(endElement); - if (endElementTag.equals(JBossSAMLConstants.ASSERTION.get())) - break; - else - throw new RuntimeException(ErrorCodes.UNKNOWN_END_ELEMENT + endElementTag); - } - - StartElement peekedElement = null; - - if (xmlEvent instanceof StartElement) { - peekedElement = (StartElement) xmlEvent; - } else { - peekedElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - } - if (peekedElement == null) - break; - - String tag = StaxParserUtil.getStartElementName(peekedElement); - - if (tag.equals(JBossSAMLConstants.SIGNATURE.get())) { - assertion.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); - continue; - } - - if (JBossSAMLConstants.ISSUER.get().equalsIgnoreCase(tag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String issuerValue = StaxParserUtil.getElementText(xmlEventReader); - NameIDType issuer = new NameIDType(); - issuer.setValue(issuerValue); - - assertion.setIssuer(issuer); - } else if (JBossSAMLConstants.SUBJECT.get().equalsIgnoreCase(tag)) { - SAMLSubjectParser subjectParser = new SAMLSubjectParser(); - assertion.setSubject((SubjectType) subjectParser.parse(xmlEventReader)); - } else if (JBossSAMLConstants.CONDITIONS.get().equalsIgnoreCase(tag)) { - SAMLConditionsParser conditionsParser = new SAMLConditionsParser(); - ConditionsType conditions = (ConditionsType) conditionsParser.parse(xmlEventReader); - - assertion.setConditions(conditions); - } else if (JBossSAMLConstants.ADVICE.get().equalsIgnoreCase(tag)) { - StaxParserUtil.bypassElementBlock(xmlEventReader); - logger.debug("SAML Advice tag is ignored"); - } else if (JBossSAMLConstants.AUTHN_STATEMENT.get().equalsIgnoreCase(tag)) { - AuthnStatementType authnStatementType = SAMLParserUtil.parseAuthnStatement(xmlEventReader); - assertion.addStatement(authnStatementType); - } else if (JBossSAMLConstants.ATTRIBUTE_STATEMENT.get().equalsIgnoreCase(tag)) { - AttributeStatementType attributeStatementType = SAMLParserUtil.parseAttributeStatement(xmlEventReader); - assertion.addStatement(attributeStatementType); - } else if (JBossSAMLConstants.STATEMENT.get().equalsIgnoreCase(tag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - - String xsiTypeValue = StaxParserUtil.getXSITypeValue(startElement); - throw new RuntimeException(ErrorCodes.UNKNOWN_XSI + xsiTypeValue); - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_TAG + tag + "::location=" + peekedElement.getLocation()); - } - return assertion; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.ASSERTION_NSURI.get()) - && localPart.equals(JBossSAMLConstants.ASSERTION.get()); - } - - private AssertionType parseBaseAttributes(StartElement nextElement) throws ParsingException { - String id = StaxParserUtil.getAttributeValue(nextElement, JBossSAMLConstants.ID.get()); - if (id == null) { - throw logger.parserRequiredAttribute(JBossSAMLConstants.ID.get()); - } - - String version = StaxParserUtil.getAttributeValue(nextElement, JBossSAMLConstants.VERSION.get()); - if (!JBossSAMLConstants.VERSION_2_0.get().equals(version)) { - throw logger.parserException(new RuntimeException( - String.format("Assertion %s required to be \"%s\"", JBossSAMLConstants.VERSION.get(), JBossSAMLConstants.VERSION_2_0.get()))); - } - - String issueInstantString = StaxParserUtil.getAttributeValue(nextElement, JBossSAMLConstants.ISSUE_INSTANT.get()); - if (issueInstantString == null) { - throw logger.parserRequiredAttribute(JBossSAMLConstants.ISSUE_INSTANT.get()); - } - XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(issueInstantString); - - return new AssertionType(id, issueInstant); - } - -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java deleted file mode 100755 index 6102e9eb1e..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.protocol.AttributeQueryType; -import org.keycloak.saml.common.ErrorCodes; -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.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.StartElement; - -/** - * Parse the {@link org.keycloak.dom.saml.v2.protocol.ArtifactResolveType} - * - * @author Anil.Saldhana@redhat.com - * @since Jul 1, 2011 - */ -public class SAMLAttributeQueryParser extends SAMLRequestAbstractParser implements ParserNamespaceSupport { - - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ATTRIBUTE_QUERY.get()); - - AttributeQueryType attributeQuery = parseBaseAttributes(startElement); - - while (xmlEventReader.hasNext()) { - // Let us peek at the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - super.parseCommonElements(startElement, xmlEventReader, attributeQuery); - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.SUBJECT.get().equals(elementName)) { - attributeQuery.setSubject(getSubject(xmlEventReader)); - } else if (JBossSAMLConstants.ATTRIBUTE.get().equals(elementName)) { - attributeQuery.add(SAMLParserUtil.parseAttribute(xmlEventReader)); - } else if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - continue; - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" - + startElement.getLocation()); - } - return attributeQuery; - } - - public boolean supports(QName qname) { - return JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()); - } - - /** - * Parse the attributes at the authnrequesttype element - * - * @param startElement - * - * @return - * - * @throws ParsingException - */ - private AttributeQueryType parseBaseAttributes(StartElement startElement) throws ParsingException { - super.parseRequiredAttributes(startElement); - AttributeQueryType authnRequest = new AttributeQueryType(id, issueInstant); - // Let us get the attributes - super.parseBaseAttributes(startElement, authnRequest); - - return authnRequest; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java deleted file mode 100755 index 8cf94829e8..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.ConditionsType; -import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; -import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; -import org.keycloak.dom.saml.v2.protocol.NameIDPolicyType; -import org.keycloak.dom.saml.v2.protocol.RequestedAuthnContextType; -import org.keycloak.saml.common.ErrorCodes; -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.ParserNamespaceSupport; -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.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import java.net.URI; - -/** - * Parse the SAML2 AuthnRequest - * - * @author Anil.Saldhana@redhat.com - * @since Nov 2, 2010 - */ -public class SAMLAuthNRequestParser extends SAMLRequestAbstractParser implements ParserNamespaceSupport { - - /** - * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} - */ - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.AUTHN_REQUEST.get()); - - AuthnRequestType authnRequest = parseBaseAttributes(startElement); - - while (xmlEventReader.hasNext()) { - // Let us peek at the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - super.parseCommonElements(startElement, xmlEventReader, authnRequest); - - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.NAMEID_POLICY.get().equals(elementName)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - authnRequest.setNameIDPolicy(getNameIDPolicy(startElement)); - } else if (JBossSAMLConstants.SUBJECT.get().equals(elementName)) { - authnRequest.setSubject(getSubject(xmlEventReader)); - } else if (JBossSAMLConstants.CONDITIONS.get().equals(elementName)) { - authnRequest.setConditions((ConditionsType) (new SAMLConditionsParser()).parse(xmlEventReader)); - } else if (JBossSAMLConstants.REQUESTED_AUTHN_CONTEXT.get().equals(elementName)) { - authnRequest.setRequestedAuthnContext(getRequestedAuthnContextType(xmlEventReader)); - } else if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.SCOPING.get().equals(elementName)) { - StaxParserUtil.bypassElementBlock(xmlEventReader, JBossSAMLConstants.SCOPING); - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" - + startElement.getLocation()); - } - return authnRequest; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()); - } - - /** - * Parse the attributes at the authnrequesttype element - * - * @param startElement - * - * @return - * - * @throws ParsingException - */ - private AuthnRequestType parseBaseAttributes(StartElement startElement) throws ParsingException { - super.parseRequiredAttributes(startElement); - AuthnRequestType authnRequest = new AuthnRequestType(id, issueInstant); - // Let us get the attributes - super.parseBaseAttributes(startElement, authnRequest); - - Attribute assertionConsumerServiceURL = startElement.getAttributeByName(new QName( - JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE_URL.get())); - if (assertionConsumerServiceURL != null) { - String uri = StaxParserUtil.getAttributeValue(assertionConsumerServiceURL); - authnRequest.setAssertionConsumerServiceURL(URI.create(uri)); - } - - Attribute assertionConsumerServiceIndex = startElement.getAttributeByName(new QName( - JBossSAMLConstants.ASSERTION_CONSUMER_SERVICE_INDEX.get())); - if (assertionConsumerServiceIndex != null) - authnRequest.setAssertionConsumerServiceIndex(Integer.parseInt(StaxParserUtil - .getAttributeValue(assertionConsumerServiceIndex))); - - Attribute protocolBinding = startElement.getAttributeByName(new QName(JBossSAMLConstants.PROTOCOL_BINDING.get())); - if (protocolBinding != null) - authnRequest.setProtocolBinding(URI.create(StaxParserUtil.getAttributeValue(protocolBinding))); - - Attribute providerName = startElement.getAttributeByName(new QName(JBossSAMLConstants.PROVIDER_NAME.get())); - if (providerName != null) - authnRequest.setProviderName(StaxParserUtil.getAttributeValue(providerName)); - - Attribute forceAuthn = startElement.getAttributeByName(new QName(JBossSAMLConstants.FORCE_AUTHN.get())); - if (forceAuthn != null) { - authnRequest.setForceAuthn(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(forceAuthn))); - } - - Attribute isPassive = startElement.getAttributeByName(new QName(JBossSAMLConstants.IS_PASSIVE.get())); - if (isPassive != null) { - authnRequest.setIsPassive(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(isPassive))); - } - - Attribute attributeConsumingServiceIndex = startElement.getAttributeByName(new QName( - JBossSAMLConstants.ATTRIBUTE_CONSUMING_SERVICE_INDEX.get())); - if (attributeConsumingServiceIndex != null) - authnRequest.setAttributeConsumingServiceIndex(Integer.parseInt(StaxParserUtil - .getAttributeValue(attributeConsumingServiceIndex))); - - return authnRequest; - } - - /** - * Get the NameIDPolicy - * - * @param startElement - * - * @return - */ - private NameIDPolicyType getNameIDPolicy(StartElement startElement) { - NameIDPolicyType nameIDPolicy = new NameIDPolicyType(); - Attribute format = startElement.getAttributeByName(new QName(JBossSAMLConstants.FORMAT.get())); - if (format != null) - nameIDPolicy.setFormat(URI.create(StaxParserUtil.getAttributeValue(format))); - - Attribute allowCreate = startElement.getAttributeByName(new QName(JBossSAMLConstants.ALLOW_CREATE.get())); - if (allowCreate != null) - nameIDPolicy.setAllowCreate(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(allowCreate))); - - return nameIDPolicy; - } - - private RequestedAuthnContextType getRequestedAuthnContextType(XMLEventReader xmlEventReader) throws ParsingException { - RequestedAuthnContextType ract = new RequestedAuthnContextType(); - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.REQUESTED_AUTHN_CONTEXT.get()); - - Attribute comparison = startElement.getAttributeByName(new QName(JBossSAMLConstants.COMPARISON.get())); - - if (comparison != null) { - ract.setComparison(AuthnContextComparisonType.fromValue(comparison.getValue())); - } - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - - if (xmlEvent instanceof EndElement) { - EndElement nextEndElement = (EndElement) xmlEvent; - if (StaxParserUtil.matches(nextEndElement, JBossSAMLConstants.REQUESTED_AUTHN_CONTEXT.get())) { - nextEndElement = StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_END_ELEMENT - + StaxParserUtil.getEndElementName(nextEndElement)); - } - - String tag = null; - - if (xmlEvent instanceof StartElement) { - StartElement peekedElement = (StartElement) xmlEvent; - tag = StaxParserUtil.getStartElementName(peekedElement); - } - - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String elName = StaxParserUtil.getStartElementName(startElement); - - if (elName.equals(JBossSAMLConstants.AUTHN_CONTEXT_CLASS_REF.get())) { - String value = StaxParserUtil.getElementText(xmlEventReader); - ract.addAuthnContextClassRef(value); - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_TAG + elName); - } - - return ract; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLConditionsParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLConditionsParser.java deleted file mode 100755 index 4e49483943..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLConditionsParser.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.AudienceRestrictionType; -import org.keycloak.dom.saml.v2.assertion.ConditionsType; -import org.keycloak.dom.saml.v2.assertion.OneTimeUseType; -import org.keycloak.saml.common.ErrorCodes; -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.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; - -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.net.URI; - -/** - * Parse the in the saml assertion - * - * @author Anil.Saldhana@redhat.com - * @since Oct 14, 2010 - */ -public class SAMLConditionsParser implements ParserNamespaceSupport { - - /** - * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} - */ - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // We are entering this method with as the next start element - // and we have to exit after seeing the end tag - - StartElement conditionsElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(conditionsElement, JBossSAMLConstants.CONDITIONS.get()); - - ConditionsType conditions = new ConditionsType(); - - String assertionNS = JBossSAMLURIConstants.ASSERTION_NSURI.get(); - - QName notBeforeQName = new QName("", JBossSAMLConstants.NOT_BEFORE.get()); - QName notBeforeQNameWithNS = new QName(assertionNS, JBossSAMLConstants.NOT_BEFORE.get()); - - QName notAfterQName = new QName("", JBossSAMLConstants.NOT_ON_OR_AFTER.get()); - QName notAfterQNameWithNS = new QName(assertionNS, JBossSAMLConstants.NOT_ON_OR_AFTER.get()); - - Attribute notBeforeAttribute = conditionsElement.getAttributeByName(notBeforeQName); - if (notBeforeAttribute == null) - notBeforeAttribute = conditionsElement.getAttributeByName(notBeforeQNameWithNS); - - Attribute notAfterAttribute = conditionsElement.getAttributeByName(notAfterQName); - if (notAfterAttribute == null) - notAfterAttribute = conditionsElement.getAttributeByName(notAfterQNameWithNS); - - if (notBeforeAttribute != null) { - String notBeforeValue = StaxParserUtil.getAttributeValue(notBeforeAttribute); - conditions.setNotBefore(XMLTimeUtil.parse(notBeforeValue)); - } - - if (notAfterAttribute != null) { - String notAfterValue = StaxParserUtil.getAttributeValue(notAfterAttribute); - conditions.setNotOnOrAfter(XMLTimeUtil.parse(notAfterValue)); - } - - // Let us find additional elements - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - - if (xmlEvent instanceof EndElement) { - EndElement nextEndElement = (EndElement) xmlEvent; - if (StaxParserUtil.matches(nextEndElement, JBossSAMLConstants.CONDITIONS.get())) { - nextEndElement = StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_END_ELEMENT - + StaxParserUtil.getEndElementName(nextEndElement)); - } - - String tag = null; - - if (xmlEvent instanceof StartElement) { - StartElement peekedElement = (StartElement) xmlEvent; - tag = StaxParserUtil.getStartElementName(peekedElement); - } - - if (JBossSAMLConstants.AUDIENCE_RESTRICTION.get().equals(tag)) { - AudienceRestrictionType audienceRestriction = getAudienceRestriction(xmlEventReader); - conditions.addCondition(audienceRestriction); - } else if (JBossSAMLConstants.ONE_TIME_USE.get().equals(tag)) { - // just parses the onetimeuse tag. until now PL has no support for onetimeuse conditions. - StaxParserUtil.getNextStartElement(xmlEventReader); - OneTimeUseType oneTimeUseCondition = new OneTimeUseType(); - conditions.addCondition(oneTimeUseCondition); - - // Get the end tag - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - StaxParserUtil.matches(endElement, JBossSAMLConstants.ONE_TIME_USE.get()); - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_TAG + tag + "::location=" + xmlEvent.getLocation()); - } - return conditions; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.ASSERTION_NSURI.get()) - && localPart.equals(JBossSAMLConstants.CONDITIONS.get()); - } - - /** - * Parse the element - * - * @param xmlEventReader - * - * @return - * - * @throws ParsingException - */ - private AudienceRestrictionType getAudienceRestriction(XMLEventReader xmlEventReader) throws ParsingException { - StartElement audienceRestElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.matches(audienceRestElement, JBossSAMLConstants.AUDIENCE_RESTRICTION.get()); - - AudienceRestrictionType audience = new AudienceRestrictionType(); - - while (xmlEventReader.hasNext()) { - StartElement audienceElement = StaxParserUtil.getNextStartElement(xmlEventReader); - if (!StaxParserUtil.matches(audienceElement, JBossSAMLConstants.AUDIENCE.get())) - break; - - if (!StaxParserUtil.hasTextAhead(xmlEventReader)) - throw new ParsingException(ErrorCodes.EXPECTED_TAG + "audienceValue"); - - String audienceValue = StaxParserUtil.getElementText(xmlEventReader); - audience.addAudience(URI.create(audienceValue)); - - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) xmlEvent; - if (StaxParserUtil.matches(endElement, JBossSAMLConstants.AUDIENCE_RESTRICTION.get())) { - StaxParserUtil.getNextEvent(xmlEventReader); // Just get the end element - break; - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_END_ELEMENT + StaxParserUtil.getEndElementName(endElement)); - } - } - return audience; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLExtensionsParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLExtensionsParser.java deleted file mode 100644 index 5f7ebbb909..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLExtensionsParser.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import org.keycloak.dom.saml.v2.protocol.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.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; - -/** - * Parses <samlp:Extensions> SAML2 element into series of DOM nodes. - * - * @author hmlnarik - */ -public class SAMLExtensionsParser implements ParserNamespaceSupport { - - private static final String EXTENSIONS = JBossSAMLConstants.EXTENSIONS.get(); - - private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - - @Override - public ExtensionsType parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, EXTENSIONS); - - ExtensionsType extensions = new ExtensionsType(); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) xmlEvent; - if (StaxParserUtil.matches(endElement, EXTENSIONS)) { - endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); - } - - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - - extensions.addExtension(StaxParserUtil.getDOMElement(xmlEventReader)); - } - - return extensions; - } - - @Override - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.PROTOCOL_NSURI.get()) - && localPart.equals(JBossSAMLConstants.EXTENSIONS.get()); - } - -} 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 b34d4043d2..b4695473f1 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,113 +16,118 @@ */ package org.keycloak.saml.processing.core.parsers.saml; +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; +import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLArtifactResolveParser; +import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLArtifactResponseParser; +import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLResponseParser; +import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLAuthNRequestParser; import org.keycloak.saml.common.ErrorCodes; 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.parsers.saml.assertion.SAMLAssertionParser; import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLEntitiesDescriptorParser; import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLEntityDescriptorParser; +import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLProtocolQNames; import org.keycloak.saml.processing.core.saml.v1.SAML11Constants; +import java.util.HashMap; +import java.util.Map; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; +import org.keycloak.saml.common.parsers.StaxParser; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionQNames; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAuthnStatementParser; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLEncryptedAssertionParser; +import java.io.InputStream; /** * Parse SAML payload * - * @author Anil.Saldhana@redhat.com * @since Oct 12, 2010 */ 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(); + + private static final QName SAML_11_ASSERTION = new QName(SAML11Constants.ASSERTION_11_NSURI, JBossSAMLConstants.ASSERTION.get()); + private static final QName SAML_11_ENCRYPTED_ASSERTION = new QName(SAML11Constants.ASSERTION_11_NSURI, JBossSAMLConstants.ENCRYPTED_ASSERTION.get()); + private static final QName SAML_11_RESPONSE = new QName(SAML11Constants.ASSERTION_11_NSURI, JBossSAMLConstants.RESPONSE__PROTOCOL.get()); + private static final QName SAML_11_REQUEST = new QName(SAML11Constants.ASSERTION_11_NSURI, JBossSAMLConstants.REQUEST.get()); + + // Since we have to support JDK 7, no lambdas are available + private interface ParserFactory { + public StaxParser create(); + } + private static final Map PARSERS = new HashMap(); + + static { + PARSERS.put(SAML_11_ASSERTION, new ParserFactory() { @Override public StaxParser create() { return new SAML11AssertionParser(); }}); + PARSERS.put(SAML_11_ENCRYPTED_ASSERTION, new ParserFactory() { @Override public StaxParser create() { return new SAML11AssertionParser(); }}); + PARSERS.put(SAML_11_RESPONSE, new ParserFactory() { @Override public StaxParser create() { return SAML_11_RESPONSE_PARSER; }}); + PARSERS.put(SAML_11_REQUEST, new ParserFactory() { @Override public StaxParser create() { return SAML_11_REQUEST_PARSER; }}); + + PARSERS.put(SAMLProtocolQNames.AUTHN_REQUEST.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLAuthNRequestParser.getInstance(); }}); + PARSERS.put(SAMLProtocolQNames.RESPONSE.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLResponseParser.getInstance(); }}); + PARSERS.put(SAMLProtocolQNames.LOGOUT_REQUEST.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLSloRequestParser.getInstance(); }}); + PARSERS.put(SAMLProtocolQNames.LOGOUT_RESPONSE.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLSloResponseParser.getInstance(); }}); + + PARSERS.put(SAMLProtocolQNames.ARTIFACT_RESOLVE.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLArtifactResolveParser.getInstance(); }}); + PARSERS.put(SAMLProtocolQNames.ARTIFACT_RESPONSE.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLArtifactResponseParser.getInstance(); }}); + + PARSERS.put(SAMLProtocolQNames.ASSERTION.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLAssertionParser.getInstance(); }}); + PARSERS.put(SAMLProtocolQNames.ENCRYPTED_ASSERTION.getQName(),new ParserFactory() { @Override public StaxParser create() { return SAMLEncryptedAssertionParser.getInstance(); }}); + + 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(SAMLProtocolQNames.ATTRIBUTE_QUERY.getQName(), new ParserFactory() { @Override public StaxParser create() { return SAMLAttributeQueryParser.getInstance(); }}); + } + + private static final SAMLParser INSTANCE = new SAMLParser(); + + public static SAMLParser getInstance() { + return INSTANCE; + } + + protected SAMLParser() { + } + /** * @see {@link org.keycloak.saml.common.parsers.ParserNamespaceSupport#parse(XMLEventReader)} */ + @Override public Object parse(XMLEventReader xmlEventReader) throws ParsingException { while (xmlEventReader.hasNext()) { XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); if (xmlEvent instanceof StartElement) { StartElement startElement = (StartElement) xmlEvent; - QName startElementName = startElement.getName(); - String nsURI = startElementName.getNamespaceURI(); + final QName name = startElement.getName(); - String localPart = startElementName.getLocalPart(); + ParserFactory pf = PARSERS.get(name); + if (pf == null) { + throw logger.parserException(new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + name + "::location=" + + startElement.getLocation())); + } - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (elementName.equalsIgnoreCase(JBossSAMLConstants.ASSERTION.get()) - || elementName.equals(JBossSAMLConstants.ENCRYPTED_ASSERTION.get())) { - if (nsURI.equals(SAML11Constants.ASSERTION_11_NSURI)) { - SAML11AssertionParser saml11AssertionParser = new SAML11AssertionParser(); - return saml11AssertionParser.parse(xmlEventReader); - } - SAMLAssertionParser assertionParser = new SAMLAssertionParser(); - return assertionParser.parse(xmlEventReader); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.AUTHN_REQUEST.get().equals(startElementName.getLocalPart())) { - SAMLAuthNRequestParser authNRequestParser = new SAMLAuthNRequestParser(); - return authNRequestParser.parse(xmlEventReader); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.LOGOUT_REQUEST.get().equals(startElementName.getLocalPart())) { - SAMLSloRequestParser sloParser = new SAMLSloRequestParser(); - return sloParser.parse(xmlEventReader); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.LOGOUT_RESPONSE.get().equals(startElementName.getLocalPart())) { - SAMLSloResponseParser sloParser = new SAMLSloResponseParser(); - return sloParser.parse(xmlEventReader); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.RESPONSE.get().equals(startElementName.getLocalPart())) { - SAMLResponseParser responseParser = new SAMLResponseParser(); - return responseParser.parse(xmlEventReader); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.REQUEST_ABSTRACT.get().equals(startElementName.getLocalPart())) { - String xsiTypeValue = StaxParserUtil.getXSITypeValue(startElement); - throw new RuntimeException(ErrorCodes.UNKNOWN_XSI + xsiTypeValue); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.ARTIFACT_RESOLVE.get().equals(startElementName.getLocalPart())) { - SAMLArtifactResolveParser artifactResolverParser = new SAMLArtifactResolveParser(); - return artifactResolverParser.parse(xmlEventReader); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.ARTIFACT_RESPONSE.get().equals(startElementName.getLocalPart())) { - SAMLArtifactResponseParser responseParser = new SAMLArtifactResponseParser(); - return responseParser.parse(xmlEventReader); - } else if (JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(nsURI) - && JBossSAMLConstants.ATTRIBUTE_QUERY.get().equals(startElementName.getLocalPart())) { - SAMLAttributeQueryParser responseParser = new SAMLAttributeQueryParser(); - return responseParser.parse(xmlEventReader); - } else if (JBossSAMLConstants.ENTITY_DESCRIPTOR.get().equals(localPart)) { - SAMLEntityDescriptorParser entityDescriptorParser = new SAMLEntityDescriptorParser(); - return entityDescriptorParser.parse(xmlEventReader); - } else if (JBossSAMLConstants.ENTITIES_DESCRIPTOR.get().equals(localPart)) { - SAMLEntitiesDescriptorParser entityDescriptorParser = new SAMLEntitiesDescriptorParser(); - return entityDescriptorParser.parse(xmlEventReader); - } else if (SAML11Constants.PROTOCOL_11_NSURI.equals(nsURI) - && JBossSAMLConstants.RESPONSE.get().equals(startElementName.getLocalPart())) { - SAML11ResponseParser responseParser = new SAML11ResponseParser(); - return responseParser.parse(xmlEventReader); - } else if (SAML11Constants.PROTOCOL_11_NSURI.equals(nsURI) - && SAML11Constants.REQUEST.equals(startElementName.getLocalPart())) { - SAML11RequestParser reqParser = new SAML11RequestParser(); - return reqParser.parse(xmlEventReader); - } else - throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" - + startElement.getLocation()); - } else { - StaxParserUtil.getNextEvent(xmlEventReader); + return pf.create().parse(xmlEventReader); } + + StaxParserUtil.getNextEvent(xmlEventReader); } + throw new RuntimeException(ErrorCodes.FAILED_PARSING + "SAML Parsing has failed"); } - - /** - * @see {@link org.keycloak.saml.common.parsers.ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return JBossSAMLURIConstants.ASSERTION_NSURI.get().equals(qname.getNamespaceURI()); - } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLRequestAbstractParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLRequestAbstractParser.java deleted file mode 100755 index 4d722a522b..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLRequestAbstractParser.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.assertion.SubjectType; -import org.keycloak.dom.saml.v2.protocol.RequestAbstractType; -import org.keycloak.saml.common.PicketLinkLogger; -import org.keycloak.saml.common.PicketLinkLoggerFactory; -import org.keycloak.saml.common.constants.JBossSAMLConstants; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; - -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; -import java.net.URI; - -/** - * Base Class for SAML Request Parsing - * - * @author Anil.Saldhana@redhat.com - * @since Nov 2, 2010 - */ -public abstract class SAMLRequestAbstractParser { - - protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - - protected String id; - - protected String version; - - protected XMLGregorianCalendar issueInstant; - - protected void parseRequiredAttributes(StartElement startElement) throws ParsingException { - Attribute idAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.ID.get())); - if (idAttr == null) - throw logger.parserRequiredAttribute("ID"); - - id = StaxParserUtil.getAttributeValue(idAttr); - - Attribute versionAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.VERSION.get())); - if (versionAttr == null) - throw logger.parserRequiredAttribute("Version"); - version = StaxParserUtil.getAttributeValue(versionAttr); - - Attribute issueInstantAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.ISSUE_INSTANT.get())); - if (issueInstantAttr == null) - throw logger.parserRequiredAttribute("IssueInstant"); - issueInstant = XMLTimeUtil.parse(StaxParserUtil.getAttributeValue(issueInstantAttr)); - } - - /** - * Parse the attributes that are common to all SAML Request Types - * - * @param startElement - * @param request - * - * @throws ParsingException - */ - protected void parseBaseAttributes(StartElement startElement, RequestAbstractType request) throws ParsingException { - Attribute destinationAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.DESTINATION.get())); - if (destinationAttr != null) - request.setDestination(URI.create(StaxParserUtil.getAttributeValue(destinationAttr))); - - Attribute consent = startElement.getAttributeByName(new QName(JBossSAMLConstants.CONSENT.get())); - if (consent != null) - request.setConsent(StaxParserUtil.getAttributeValue(consent)); - } - - protected void parseCommonElements(StartElement startElement, XMLEventReader xmlEventReader, RequestAbstractType request) - throws ParsingException { - if (startElement == null) - throw logger.parserNullStartElement(); - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - NameIDType issuer = new NameIDType(); - issuer.setValue(StaxParserUtil.getElementText(xmlEventReader)); - request.setIssuer(issuer); - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - request.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - request.setExtensions(new SAMLExtensionsParser().parse(xmlEventReader)); - } - } - - protected SubjectType getSubject(XMLEventReader xmlEventReader) throws ParsingException { - SAMLSubjectParser subjectParser = new SAMLSubjectParser(); - return (SubjectType) subjectParser.parse(xmlEventReader); - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java deleted file mode 100755 index 92eaf90228..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.AssertionType; -import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType; -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.protocol.ResponseType; -import org.keycloak.dom.saml.v2.protocol.ResponseType.RTChoiceType; -import org.keycloak.dom.saml.v2.protocol.StatusResponseType; -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.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.StartElement; - -/** - * Parse the SAML Response - * - * @author Anil.Saldhana@redhat.com - * @since Nov 2, 2010 - */ -public class SAMLResponseParser extends SAMLStatusResponseTypeParser implements ParserNamespaceSupport { - - private final String RESPONSE = JBossSAMLConstants.RESPONSE.get(); - - /** - * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} - */ - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, RESPONSE); - - ResponseType response = (ResponseType) parseBaseAttributes(startElement); - - while (xmlEventReader.hasNext()) { - // Let us peek at the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - NameIDType issuer = new NameIDType(); - issuer.setValue(StaxParserUtil.getElementText(xmlEventReader)); - response.setIssuer(issuer); - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - Element sig = StaxParserUtil.getDOMElement(xmlEventReader); - response.setSignature(sig); - } else if (JBossSAMLConstants.ASSERTION.get().equals(elementName)) { - SAMLAssertionParser assertionParser = new SAMLAssertionParser(); - response.addAssertion(new RTChoiceType((AssertionType) assertionParser.parse(xmlEventReader))); - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser(); - response.setExtensions(extensionsParser.parse(xmlEventReader)); - } else if (JBossSAMLConstants.STATUS.get().equals(elementName)) { - response.setStatus(parseStatus(xmlEventReader)); - } else if (JBossSAMLConstants.ENCRYPTED_ASSERTION.get().equals(elementName)) { - Element encryptedAssertion = StaxParserUtil.getDOMElement(xmlEventReader); - response.addAssertion(new RTChoiceType(new EncryptedAssertionType(encryptedAssertion))); - } else - throw logger.parserUnknownTag(elementName, startElement.getLocation()); - } - - return response; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()) - && RESPONSE.equals(qname.getLocalPart()); - } - - /** - * Parse the attributes at the response element - * - * @param startElement - * - * @return - * - * @throws org.keycloak.saml.common.exceptions.ConfigurationException - */ - protected StatusResponseType parseBaseAttributes(StartElement startElement) throws ParsingException { - ResponseType response = new ResponseType(super.parseBaseAttributes(startElement)); - return response; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java deleted file mode 100755 index ba154284e1..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; -import org.keycloak.saml.common.constants.JBossSAMLConstants; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.StartElement; - -import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI; - -/** - * Parse the Single Log Out requests - * - * @author Anil.Saldhana@redhat.com - * @since Nov 3, 2010 - */ -public class SAMLSloRequestParser extends SAMLRequestAbstractParser implements ParserNamespaceSupport { - - /** - * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} - */ - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.LOGOUT_REQUEST.get()); - - LogoutRequestType logoutRequest = parseBaseAttributes(startElement); - - while (xmlEventReader.hasNext()) { - // Let us peek at the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String elementName = StaxParserUtil.getStartElementName(startElement); - - parseCommonElements(startElement, xmlEventReader, logoutRequest); - - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.SESSION_INDEX.get().equals(elementName)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - logoutRequest.addSessionIndex(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.NAMEID.get().equals(elementName)) { - NameIDType nameID = SAMLParserUtil.parseNameIDType(xmlEventReader); - logoutRequest.setNameID(nameID); - } else if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - continue; - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - continue; - } else - throw logger.parserUnknownTag(elementName, startElement.getLocation()); - } - return logoutRequest; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()) - && JBossSAMLConstants.LOGOUT_REQUEST.get().equals(qname.getLocalPart()); - } - - /** - * Parse the attributes at the log out request element - * - * @param startElement - * - * @return - * - * @throws ParsingException - */ - private LogoutRequestType parseBaseAttributes(StartElement startElement) throws ParsingException { - super.parseRequiredAttributes(startElement); - LogoutRequestType logoutRequest = new LogoutRequestType(id, issueInstant); - // Let us get the attributes - super.parseBaseAttributes(startElement, logoutRequest); - - Attribute reason = startElement.getAttributeByName(new QName(JBossSAMLConstants.REASON.get())); - if (reason != null) - logoutRequest.setReason(StaxParserUtil.getAttributeValue(reason)); - - Attribute notOnOrAfter = startElement.getAttributeByName(new QName(JBossSAMLConstants.NOT_ON_OR_AFTER.get())); - if (notOnOrAfter != null) - logoutRequest.setNotOnOrAfter(XMLTimeUtil.parse(StaxParserUtil.getAttributeValue(notOnOrAfter))); - return logoutRequest; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java deleted file mode 100755 index 2070730ffc..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.protocol.StatusResponseType; -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.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.events.StartElement; - -import static org.keycloak.saml.common.constants.JBossSAMLConstants.LOGOUT_RESPONSE; - -/** - * Parse the SLO Response - * - * @author Anil.Saldhana@redhat.com - * @since Nov 3, 2010 - */ -public class SAMLSloResponseParser extends SAMLStatusResponseTypeParser implements ParserNamespaceSupport { - - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - // Get the startelement - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, LOGOUT_RESPONSE.get()); - - StatusResponseType response = parseBaseAttributes(startElement); - - while (xmlEventReader.hasNext()) { - // Let us peek at the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String elementName = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.ISSUER.get().equals(elementName)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - NameIDType issuer = new NameIDType(); - issuer.setValue(StaxParserUtil.getElementText(xmlEventReader)); - response.setIssuer(issuer); - } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { - StaxParserUtil.bypassElementBlock(xmlEventReader, JBossSAMLConstants.SIGNATURE); - } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { - SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser(); - response.setExtensions(extensionsParser.parse(xmlEventReader)); - } else if (JBossSAMLConstants.STATUS.get().equals(elementName)) { - response.setStatus(parseStatus(xmlEventReader)); - } - } - return response; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - return JBossSAMLURIConstants.PROTOCOL_NSURI.get().equals(qname.getNamespaceURI()) - && LOGOUT_RESPONSE.get().equals(qname.getLocalPart()); - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLStatusResponseTypeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLStatusResponseTypeParser.java deleted file mode 100755 index ef03929113..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLStatusResponseTypeParser.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.protocol.StatusCodeType; -import org.keycloak.dom.saml.v2.protocol.StatusDetailType; -import org.keycloak.dom.saml.v2.protocol.StatusResponseType; -import org.keycloak.dom.saml.v2.protocol.StatusType; -import org.keycloak.saml.common.PicketLinkLogger; -import org.keycloak.saml.common.PicketLinkLoggerFactory; -import org.keycloak.saml.common.constants.JBossSAMLConstants; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.common.util.StringUtil; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; -import org.w3c.dom.Element; - -import javax.xml.datatype.XMLGregorianCalendar; -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.net.URI; - -/** - * Base Class for all Response Type parsing for SAML2 - * - * @author Anil.Saldhana@redhat.com - * @since Nov 2, 2010 - */ -public abstract class SAMLStatusResponseTypeParser { - - protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - - /** - * Parse the attributes that are common to all SAML Response Types - * - * @param startElement - * @param response - * - * @throws org.keycloak.saml.common.exceptions.ParsingException - */ - protected StatusResponseType parseBaseAttributes(StartElement startElement) throws ParsingException { - Attribute idAttr = startElement.getAttributeByName(new QName(JBossSAMLConstants.ID.get())); - if (idAttr == null) - throw logger.parserRequiredAttribute("ID"); - String id = StaxParserUtil.getAttributeValue(idAttr); - - Attribute version = startElement.getAttributeByName(new QName(JBossSAMLConstants.VERSION.get())); - if (version == null) - throw logger.parserRequiredAttribute("Version"); - - StringUtil.match(JBossSAMLConstants.VERSION_2_0.get(), StaxParserUtil.getAttributeValue(version)); - - Attribute issueInstant = startElement.getAttributeByName(new QName(JBossSAMLConstants.ISSUE_INSTANT.get())); - if (issueInstant == null) - throw logger.parserRequiredAttribute("IssueInstant"); - XMLGregorianCalendar issueInstantVal = XMLTimeUtil.parse(StaxParserUtil.getAttributeValue(issueInstant)); - - StatusResponseType response = new StatusResponseType(id, issueInstantVal); - - Attribute destination = startElement.getAttributeByName(new QName(JBossSAMLConstants.DESTINATION.get())); - if (destination != null) - response.setDestination(StaxParserUtil.getAttributeValue(destination)); - - Attribute consent = startElement.getAttributeByName(new QName(JBossSAMLConstants.CONSENT.get())); - if (consent != null) - response.setConsent(StaxParserUtil.getAttributeValue(consent)); - - Attribute inResponseTo = startElement.getAttributeByName(new QName(JBossSAMLConstants.IN_RESPONSE_TO.get())); - if (inResponseTo != null) - response.setInResponseTo(StaxParserUtil.getAttributeValue(inResponseTo)); - return response; - } - - /** - * Parse the status element - * - * @param xmlEventReader - * - * @return - * - * @throws ParsingException - */ - protected StatusType parseStatus(XMLEventReader xmlEventReader) throws ParsingException { - // Get the Start Element - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String STATUS = JBossSAMLConstants.STATUS.get(); - StaxParserUtil.validate(startElement, STATUS); - - StatusType status = new StatusType(); - - while (xmlEventReader.hasNext()) { - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - - if (startElement == null) - break; - - QName startElementName = startElement.getName(); - String elementTag = startElementName.getLocalPart(); - - StatusCodeType statusCode = new StatusCodeType(); - - if (JBossSAMLConstants.STATUS_CODE.get().equals(elementTag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - if (startElement == null) - break; - Attribute valueAttr = startElement.getAttributeByName(new QName("Value")); - if (valueAttr != null) { - statusCode.setValue(URI.create(StaxParserUtil.getAttributeValue(valueAttr))); - } - status.setStatusCode(statusCode); - - // Peek at the next start element to see if it is status code - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) { - // Go to Status code end element. - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - if (endElement != null) { - StaxParserUtil.validate(endElement, JBossSAMLConstants.STATUS_CODE.get()); - } - continue; - } - elementTag = startElement.getName().getLocalPart(); - if (JBossSAMLConstants.STATUS_CODE.get().equals(elementTag)) { - StatusCodeType subStatusCodeType = new StatusCodeType(); - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - Attribute subValueAttr = startElement.getAttributeByName(new QName("Value")); - if (subValueAttr != null) { - subStatusCodeType.setValue(URI.create(StaxParserUtil.getAttributeValue(subValueAttr))); - } - statusCode.setStatusCode(subStatusCodeType); - - // Go to Status code end element. - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.STATUS_CODE.get()); - continue; - } - } - if (JBossSAMLConstants.STATUS_MESSAGE.get().equals(elementTag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - if (startElement == null) - break; - status.setStatusMessage(StaxParserUtil.getElementText(xmlEventReader)); - } - - if (JBossSAMLConstants.STATUS_DETAIL.get().equals(elementTag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - if (startElement == null) - break; - Element domElement = StaxParserUtil.getDOMElement(xmlEventReader); - StatusDetailType statusDetailType = new StatusDetailType(); - statusDetailType.addStatusDetail(domElement); - status.setStatusDetail(statusDetailType); - } - - // Get the next end element - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - if (StaxParserUtil.matches(endElement, STATUS)) - break; - else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); - } else - break; - } - return status; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSubjectParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSubjectParser.java deleted file mode 100755 index da801db73c..0000000000 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSubjectParser.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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; - -import org.keycloak.dom.saml.v2.assertion.EncryptedElementType; -import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationDataType; -import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationType; -import org.keycloak.dom.saml.v2.assertion.SubjectType; -import org.keycloak.dom.xmlsec.w3.xmldsig.KeyInfoType; -import org.keycloak.saml.common.ErrorCodes; -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.constants.WSTrustConstants; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.ParserNamespaceSupport; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; -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; - -/** - * Parse the saml subject - * - * @author Anil.Saldhana@redhat.com - * @since Oct 12, 2010 - */ -public class SAMLSubjectParser implements ParserNamespaceSupport { - - private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - - /** - * @see {@link ParserNamespaceSupport#parse(XMLEventReader)} - */ - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - StaxParserUtil.getNextEvent(xmlEventReader); - - SubjectType subject = new SubjectType(); - - // Peek at the next event - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) xmlEvent; - if (StaxParserUtil.matches(endElement, JBossSAMLConstants.SUBJECT.get())) { - endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); - } - - StartElement peekedElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (peekedElement == null) - break; - - String tag = StaxParserUtil.getStartElementName(peekedElement); - - if (JBossSAMLConstants.NAMEID.get().equalsIgnoreCase(tag)) { - NameIDType nameID = SAMLParserUtil.parseNameIDType(xmlEventReader); - SubjectType.STSubType subType = new SubjectType.STSubType(); - subType.addBaseID(nameID); - subject.setSubType(subType); - } else if (JBossSAMLConstants.BASEID.get().equalsIgnoreCase(tag)) { - throw new ParsingException(ErrorCodes.UNSUPPORTED_TYPE + JBossSAMLConstants.BASEID.get()); - } else if (JBossSAMLConstants.ENCRYPTED_ID.get().equals(tag)) { - Element domElement = StaxParserUtil.getDOMElement(xmlEventReader); - SubjectType.STSubType subType = new SubjectType.STSubType(); - subType.setEncryptedID(new EncryptedElementType(domElement)); - subject.setSubType(subType); - } else if (JBossSAMLConstants.SUBJECT_CONFIRMATION.get().equalsIgnoreCase(tag)) { - StartElement subjectConfirmationElement = StaxParserUtil.getNextStartElement(xmlEventReader); - Attribute method = subjectConfirmationElement.getAttributeByName(new QName(JBossSAMLConstants.METHOD.get())); - - SubjectConfirmationType subjectConfirmationType = new SubjectConfirmationType(); - - if (method != null) { - subjectConfirmationType.setMethod(StaxParserUtil.getAttributeValue(method)); - } - - // There may be additional things under subject confirmation - xmlEvent = StaxParserUtil.peek(xmlEventReader); - - while (xmlEventReader.hasNext()) { - xmlEvent = StaxParserUtil.peek(xmlEventReader); - - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) xmlEvent; - if (StaxParserUtil.matches(endElement, JBossSAMLConstants.SUBJECT_CONFIRMATION.get())) { - StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); - } - - peekedElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - - if (peekedElement == null) - break; - - String startTag = StaxParserUtil.getStartElementName(peekedElement); - - if (startTag.equals(JBossSAMLConstants.NAMEID.get())) { - NameIDType nameID = SAMLParserUtil.parseNameIDType(xmlEventReader); - subjectConfirmationType.setNameID(nameID); - } else if (JBossSAMLConstants.BASEID.get().equalsIgnoreCase(tag)) { - throw logger.unsupportedType(JBossSAMLConstants.BASEID.get()); - } else if (JBossSAMLConstants.ENCRYPTED_ID.get().equals(tag)) { - Element domElement = StaxParserUtil.getDOMElement(xmlEventReader); - subjectConfirmationType.setEncryptedID(new EncryptedElementType(domElement)); - } else if (startTag.equals(JBossSAMLConstants.SUBJECT_CONFIRMATION_DATA.get())) { - SubjectConfirmationDataType subjectConfirmationData = parseSubjectConfirmationData(xmlEventReader); - subjectConfirmationType.setSubjectConfirmationData(subjectConfirmationData); - } - } - - subject.addConfirmation(subjectConfirmationType); - } else - throw logger.parserUnknownTag(tag, peekedElement.getLocation()); - } - return subject; - } - - /** - * @see {@link ParserNamespaceSupport#supports(QName)} - */ - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.ASSERTION_NSURI.get()) && localPart.equals(JBossSAMLConstants.SUBJECT.get()); - } - - private SubjectConfirmationDataType parseSubjectConfirmationData(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.SUBJECT_CONFIRMATION_DATA.get()); - - SubjectConfirmationDataType subjectConfirmationData = new SubjectConfirmationDataType(); - - Attribute inResponseTo = startElement.getAttributeByName(new QName(JBossSAMLConstants.IN_RESPONSE_TO.get())); - if (inResponseTo != null) { - subjectConfirmationData.setInResponseTo(StaxParserUtil.getAttributeValue(inResponseTo)); - } - - Attribute notBefore = startElement.getAttributeByName(new QName(JBossSAMLConstants.NOT_BEFORE.get())); - if (notBefore != null) { - subjectConfirmationData.setNotBefore(XMLTimeUtil.parse(StaxParserUtil.getAttributeValue(notBefore))); - } - - Attribute notOnOrAfter = startElement.getAttributeByName(new QName(JBossSAMLConstants.NOT_ON_OR_AFTER.get())); - if (notOnOrAfter != null) { - subjectConfirmationData.setNotOnOrAfter(XMLTimeUtil.parse(StaxParserUtil.getAttributeValue(notOnOrAfter))); - } - - Attribute recipient = startElement.getAttributeByName(new QName(JBossSAMLConstants.RECIPIENT.get())); - if (recipient != null) { - subjectConfirmationData.setRecipient(StaxParserUtil.getAttributeValue(recipient)); - } - - Attribute address = startElement.getAttributeByName(new QName(JBossSAMLConstants.ADDRESS.get())); - if (address != null) { - subjectConfirmationData.setAddress(StaxParserUtil.getAttributeValue(address)); - } - - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (!(xmlEvent instanceof EndElement)) { - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - String tag = StaxParserUtil.getStartElementName(startElement); - if (tag.equals(WSTrustConstants.XMLDSig.KEYINFO)) { - KeyInfoType keyInfo = SAMLParserUtil.parseKeyInfo(xmlEventReader); - subjectConfirmationData.setAnyType(keyInfo); - } else if (tag.equals(WSTrustConstants.XMLEnc.ENCRYPTED_KEY)) { - subjectConfirmationData.setAnyType(StaxParserUtil.getDOMElement(xmlEventReader)); - } else - throw logger.parserUnknownTag(tag, startElement.getLocation()); - } - - // Get the end tag - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - StaxParserUtil.matches(endElement, JBossSAMLConstants.SUBJECT_CONFIRMATION_DATA.get()); - return subjectConfirmationData; - } -} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/AbstractStaxSamlAssertionParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/AbstractStaxSamlAssertionParser.java new file mode 100644 index 0000000000..0c3ac5847d --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/AbstractStaxSamlAssertionParser.java @@ -0,0 +1,40 @@ +/* + * 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.assertion; + +import org.keycloak.saml.common.parsers.AbstractStaxParser; +import org.keycloak.saml.processing.core.parsers.util.QNameEnumLookup; +import javax.xml.namespace.QName; + +/** + * + * @author hmlnarik + */ +public abstract class AbstractStaxSamlAssertionParser extends AbstractStaxParser { + + protected static final QNameEnumLookup LOOKUP = new QNameEnumLookup(SAMLAssertionQNames.values()); + + public AbstractStaxSamlAssertionParser(SAMLAssertionQNames expectedStartElement) { + super(expectedStartElement.getQName(), SAMLAssertionQNames.UNKNOWN_ELEMENT); + } + + @Override + protected SAMLAssertionQNames getElementFromName(QName name) { + return LOOKUP.from(name); + } + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionParser.java new file mode 100755 index 0000000000..91f074db0a --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionParser.java @@ -0,0 +1,103 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.AssertionType; +import org.keycloak.saml.common.ErrorCodes; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * Parse the saml assertion + * + * @since Oct 12, 2010 + */ +public class SAMLAssertionParser extends AbstractStaxSamlAssertionParser { + + private static final String VERSION_2_0 = "2.0"; + + private static final SAMLAssertionParser INSTANCE = new SAMLAssertionParser(); + + private SAMLAssertionParser() { + super(SAMLAssertionQNames.ASSERTION); + } + + public static SAMLAssertionParser getInstance() { + return INSTANCE; + } + + @Override + protected AssertionType instantiateElement(XMLEventReader xmlEventReader, StartElement nextElement) throws ParsingException { + SAMLParserUtil.validateAttributeValue(nextElement, SAMLAssertionQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(nextElement, SAMLAssertionQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(nextElement, SAMLAssertionQNames.ATTR_ISSUE_INSTANT)); + + return new AssertionType(id, issueInstant); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AssertionType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + target.setIssuer(SAMLParserUtil.parseNameIDType(xmlEventReader)); + break; + + case SIGNATURE: + target.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); + break; + + case SUBJECT: + target.setSubject(SAMLSubjectParser.getInstance().parse(xmlEventReader)); + break; + + case CONDITIONS: + target.setConditions(SAMLConditionsParser.getInstance().parse(xmlEventReader)); + break; + + case ADVICE: + StaxParserUtil.bypassElementBlock(xmlEventReader); + // Ignored + break; + + case STATEMENT: + elementDetail = StaxParserUtil.getNextStartElement(xmlEventReader); + String xsiTypeValue = StaxParserUtil.getXSITypeValue(elementDetail); + throw new RuntimeException(ErrorCodes.UNKNOWN_XSI + xsiTypeValue); + + case AUTHN_STATEMENT: + target.addStatement(SAMLAuthnStatementParser.getInstance().parse(xmlEventReader)); + break; + + case AUTHZ_DECISION_STATEMENT: + StaxParserUtil.bypassElementBlock(xmlEventReader); + // Ignored + break; + + case ATTRIBUTE_STATEMENT: + target.addStatement(SAMLAttributeStatementParser.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/assertion/SAMLAssertionQNames.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionQNames.java new file mode 100644 index 0000000000..9e18f670a8 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionQNames.java @@ -0,0 +1,116 @@ +/* + * 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.assertion; + +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.saml.processing.core.parsers.saml.xmldsig.XmlDSigQNames; +import org.keycloak.saml.processing.core.parsers.util.HasQName; +import javax.xml.namespace.QName; + +/** + * Elements and attribute names from saml-schema-assertion-2.0.xsd + * @author hmlnarik + */ +public enum SAMLAssertionQNames implements HasQName { + + ACTION("Action"), + ADVICE("Advice"), + ASSERTION("Assertion"), + ASSERTION_ID_REF("AssertionIDRef"), + ASSERTION_URI_REF("AssertionURIRef"), + ATTRIBUTE("Attribute"), + ATTRIBUTE_STATEMENT("AttributeStatement"), + ATTRIBUTE_VALUE("AttributeValue"), + AUDIENCE("Audience"), + AUDIENCE_RESTRICTION("AudienceRestriction"), + AUTHENTICATING_AUTHORITY("AuthenticatingAuthority"), + AUTHN_CONTEXT("AuthnContext"), + AUTHN_CONTEXT_CLASS_REF("AuthnContextClassRef"), + AUTHN_CONTEXT_DECL("AuthnContextDecl"), + AUTHN_CONTEXT_DECL_REF("AuthnContextDeclRef"), + AUTHN_STATEMENT("AuthnStatement"), + AUTHZ_DECISION_STATEMENT("AuthzDecisionStatement"), + BASEID("BaseID"), + CONDITION("Condition"), + CONDITIONS("Conditions"), + ENCRYPTED_ASSERTION("EncryptedAssertion"), + ENCRYPTED_ATTRIBUTE("EncryptedAttribute"), + ENCRYPTED_ID("EncryptedID"), + EVIDENCE("Evidence"), + ISSUER("Issuer"), + NAMEID("NameID"), + ONE_TIME_USE("OneTimeUse"), + PROXY_RESTRICTION("ProxyRestriction"), + STATEMENT("Statement"), + SUBJECT_CONFIRMATION_DATA("SubjectConfirmationData"), + SUBJECT_CONFIRMATION("SubjectConfirmation"), + SUBJECT_LOCALITY("SubjectLocality"), + SUBJECT("Subject"), + + // Attribute names + ATTR_ADDRESS(null, "Address"), + ATTR_AUTHN_INSTANT(null, "AuthnInstant"), + ATTR_DNS_NAME(null, "DNSName"), + ATTR_FORMAT(null, "Format"), + ATTR_FRIENDLY_NAME(null, "FriendlyName"), + ATTR_ID(null, "ID"), + ATTR_IN_RESPONSE_TO(null, "InResponseTo"), + ATTR_ISSUE_INSTANT(null, "IssueInstant"), + ATTR_METHOD(null, "Method"), + ATTR_NAME(null, "Name"), + ATTR_NAME_FORMAT(null, "NameFormat"), + ATTR_NAME_QUALIFIER(null, "NameQualifier"), + ATTR_NOT_BEFORE(null, "NotBefore"), + ATTR_NOT_ON_OR_AFTER(null, "NotOnOrAfter"), + ATTR_RECIPIENT(null, "Recipient"), + ATTR_SESSION_INDEX(null, "SessionIndex"), + ATTR_SP_PROVIDED_ID(null, "SPProvidedID"), + ATTR_SP_NAME_QUALIFIER(null, "SPNameQualifier"), + ATTR_VERSION(null, "Version"), + + // Elements from other namespaces that can be direct subelements of this namespace's elements + KEY_INFO(XmlDSigQNames.KEY_INFO), + SIGNATURE(XmlDSigQNames.SIGNATURE), + + ATTR_X500_ENCODING(JBossSAMLURIConstants.X500_NSURI, "Encoding"), + + UNKNOWN_ELEMENT("") + ; + + private final QName qName; + + private SAMLAssertionQNames(String localName) { + this(JBossSAMLURIConstants.ASSERTION_NSURI, localName); + } + + private SAMLAssertionQNames(HasQName source) { + this.qName = source.getQName(); + } + + private SAMLAssertionQNames(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/assertion/SAMLAttributeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeParser.java new file mode 100644 index 0000000000..2b8fa82633 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeParser.java @@ -0,0 +1,69 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.AttributeType; +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; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLAttributeParser extends AbstractStaxSamlAssertionParser { + + private static final SAMLAttributeParser INSTANCE = new SAMLAttributeParser(); + + private SAMLAttributeParser() { + super(SAMLAssertionQNames.ATTRIBUTE); + } + + public static SAMLAttributeParser getInstance() { + return INSTANCE; + } + + @Override + protected AttributeType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + String name = StaxParserUtil.getRequiredAttributeValue(element, SAMLAssertionQNames.ATTR_NAME); + final AttributeType attribute = new AttributeType(name); + + attribute.setFriendlyName(StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_FRIENDLY_NAME)); + attribute.setNameFormat(StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_NAME_FORMAT)); + + final String x500Encoding = StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_X500_ENCODING); + if (x500Encoding != null) { + attribute.getOtherAttributes().put(SAMLAssertionQNames.ATTR_X500_ENCODING.getQName(), x500Encoding); + } + + return attribute; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AttributeType target, SAMLAssertionQNames 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/assertion/SAMLAttributeStatementParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeStatementParser.java new file mode 100644 index 0000000000..162d7a5a81 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeStatementParser.java @@ -0,0 +1,59 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType.ASTChoiceType; +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; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLAttributeStatementParser extends AbstractStaxSamlAssertionParser { + + private static final SAMLAttributeStatementParser INSTANCE = new SAMLAttributeStatementParser(); + + private SAMLAttributeStatementParser() { + super(SAMLAssertionQNames.ATTRIBUTE_STATEMENT); + } + + public static SAMLAttributeStatementParser getInstance() { + return INSTANCE; + } + + @Override + protected AttributeStatementType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new AttributeStatementType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AttributeStatementType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ATTRIBUTE: + target.addAttribute(new ASTChoiceType(SAMLAttributeParser.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/assertion/SAMLAttributeValueParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeValueParser.java new file mode 100644 index 0000000000..272c03282d --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeValueParser.java @@ -0,0 +1,138 @@ +/* + * 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.assertion; + +import org.keycloak.saml.common.PicketLinkLogger; +import org.keycloak.saml.common.PicketLinkLoggerFactory; +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.parsers.StaxParser; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import java.io.StringWriter; +import java.util.Objects; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +/** + * + */ +public class SAMLAttributeValueParser implements StaxParser { + + private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); + + private static final SAMLAttributeValueParser INSTANCE = new SAMLAttributeValueParser(); + private static final QName NIL = new QName(JBossSAMLURIConstants.XSI_NSURI.get(), "nil", JBossSAMLURIConstants.XSI_PREFIX.get()); + private static final QName XSI_TYPE = new QName(JBossSAMLURIConstants.XSI_NSURI.get(), "type", JBossSAMLURIConstants.XSI_PREFIX.get()); + + public static SAMLAttributeValueParser getInstance() { + return INSTANCE; + } + + @Override + public Object parse(XMLEventReader xmlEventReader) throws ParsingException { + StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); + StaxParserUtil.validate(element, SAMLAssertionQNames.ATTRIBUTE_VALUE.getQName()); + + Attribute nil = element.getAttributeByName(NIL); + if (nil != null) { + String nilValue = StaxParserUtil.getAttributeValue(nil); + if (nilValue != null && (nilValue.equalsIgnoreCase("true") || nilValue.equals("1"))) { + String elementText = StaxParserUtil.getElementText(xmlEventReader); + if (elementText == null || elementText.isEmpty()) { + return null; + } else { + throw logger.nullValueError("nil attribute is not in SAML20 format"); + } + } else { + throw logger.parserRequiredAttribute(JBossSAMLURIConstants.XSI_PREFIX.get() + ":nil"); + } + } + + Attribute type = element.getAttributeByName(XSI_TYPE); + if (type == null) { + if (StaxParserUtil.hasTextAhead(xmlEventReader)) { + return StaxParserUtil.getElementText(xmlEventReader); + } + // Else we may have Child Element + XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); + if (xmlEvent instanceof StartElement) { + element = (StartElement) xmlEvent; + final QName qName = element.getName(); + if (Objects.equals(qName, SAMLAssertionQNames.NAMEID.getQName())) { + return SAMLParserUtil.parseNameIDType(xmlEventReader); + } + } else if (xmlEvent instanceof EndElement) { + return ""; + } + + // when no type attribute assigned -> assume anyType + return parseAnyTypeAsString(xmlEventReader); + } + + // RK Added an additional type check for base64Binary type as calheers is passing this type + String typeValue = StaxParserUtil.getAttributeValue(type); + if (typeValue.contains(":string")) { + return StaxParserUtil.getElementText(xmlEventReader); + } else if (typeValue.contains(":anyType")) { + return parseAnyTypeAsString(xmlEventReader); + } else if(typeValue.contains(":base64Binary")){ + return StaxParserUtil.getElementText(xmlEventReader); + } else if(typeValue.contains(":boolean")){ + return StaxParserUtil.getElementText(xmlEventReader); + } + + throw logger.parserUnknownXSI(typeValue); + } + + public static String parseAnyTypeAsString(XMLEventReader xmlEventReader) throws ParsingException { + try { + XMLEvent event = xmlEventReader.peek(); + if (event.isStartElement()) { + event = xmlEventReader.nextTag(); + StringWriter sw = new StringWriter(); + XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(sw); + //QName tagName = event.asStartElement().getName(); + int tagLevel = 1; + do { + writer.add(event); + event = (XMLEvent) xmlEventReader.next(); + if (event.isStartElement()) { + tagLevel++; + } + if (event.isEndElement()) { + tagLevel--; + } + } while (xmlEventReader.hasNext() && tagLevel > 0); + writer.add(event); + writer.flush(); + return sw.toString(); + } else { + return StaxParserUtil.getElementText(xmlEventReader); + } + } catch (Exception e) { + throw logger.parserError(e); + } + } + +} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAudienceRestrictionParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAudienceRestrictionParser.java new file mode 100644 index 0000000000..a1d5827695 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAudienceRestrictionParser.java @@ -0,0 +1,61 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.AudienceRestrictionType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import java.net.URI; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLAudienceRestrictionParser extends AbstractStaxSamlAssertionParser { + + private static final SAMLAudienceRestrictionParser INSTANCE = new SAMLAudienceRestrictionParser(); + + private SAMLAudienceRestrictionParser() { + super(SAMLAssertionQNames.AUDIENCE_RESTRICTION); + } + + public static SAMLAudienceRestrictionParser getInstance() { + return INSTANCE; + } + + @Override + protected AudienceRestrictionType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new AudienceRestrictionType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AudienceRestrictionType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case AUDIENCE: + StaxParserUtil.advance(xmlEventReader); + String audienceValue = StaxParserUtil.getElementText(xmlEventReader); + target.addAudience(URI.create(audienceValue)); + 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/assertion/SAMLAuthnContextParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAuthnContextParser.java new file mode 100644 index 0000000000..16a83bcc1b --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAuthnContextParser.java @@ -0,0 +1,94 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.AuthnContextClassRefType; +import org.keycloak.dom.saml.v2.assertion.AuthnContextDeclRefType; +import org.keycloak.dom.saml.v2.assertion.AuthnContextDeclType; +import org.keycloak.dom.saml.v2.assertion.AuthnContextType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import java.net.URI; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import org.w3c.dom.Element; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLAuthnContextParser extends AbstractStaxSamlAssertionParser { + + private static final SAMLAuthnContextParser INSTANCE = new SAMLAuthnContextParser(); + + private SAMLAuthnContextParser() { + super(SAMLAssertionQNames.AUTHN_CONTEXT); + } + + public static SAMLAuthnContextParser getInstance() { + return INSTANCE; + } + + @Override + protected AuthnContextType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new AuthnContextType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AuthnContextType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + String text; + AuthnContextType.AuthnContextTypeSequence authnContextSequence; + + switch (element) { + case AUTHN_CONTEXT_DECL: + Element dom = StaxParserUtil.getDOMElement(xmlEventReader); + AuthnContextDeclType authnContextDecl = new AuthnContextDeclType(dom); + authnContextSequence = target.getSequence() != null ? target.getSequence() : target.new AuthnContextTypeSequence(); + authnContextSequence.setAuthnContextDecl(authnContextDecl); + target.setSequence(authnContextSequence); + break; + + case AUTHN_CONTEXT_DECL_REF: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + AuthnContextDeclRefType authnContextDeclRef = new AuthnContextDeclRefType(URI.create(text)); + target.addURIType(authnContextDeclRef); + break; + + case AUTHN_CONTEXT_CLASS_REF: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + AuthnContextClassRefType authnContextClassRef = new AuthnContextClassRefType(URI.create(text)); + + authnContextSequence = target.getSequence() != null ? target.getSequence() : target.new AuthnContextTypeSequence(); + authnContextSequence.setClassRef(authnContextClassRef); + + target.setSequence(authnContextSequence); + break; + + case AUTHENTICATING_AUTHORITY: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.addAuthenticatingAuthority(URI.create(text)); + 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/assertion/SAMLAuthnStatementParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAuthnStatementParser.java new file mode 100644 index 0000000000..6e50b449cc --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAuthnStatementParser.java @@ -0,0 +1,76 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.AuthnStatementType; +import org.keycloak.dom.saml.v2.assertion.SubjectLocalityType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLAuthnStatementParser extends AbstractStaxSamlAssertionParser { + + private static final SAMLAuthnStatementParser INSTANCE = new SAMLAuthnStatementParser(); + + private SAMLAuthnStatementParser() { + super(SAMLAssertionQNames.AUTHN_STATEMENT); + } + + public static SAMLAuthnStatementParser getInstance() { + return INSTANCE; + } + + @Override + protected AuthnStatementType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + XMLGregorianCalendar authnInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(element, SAMLAssertionQNames.ATTR_AUTHN_INSTANT)); + AuthnStatementType res = new AuthnStatementType(authnInstant); + + res.setSessionIndex(StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_SESSION_INDEX)); + res.setSessionNotOnOrAfter(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLAssertionQNames.ATTR_NOT_ON_OR_AFTER)); + return res; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AuthnStatementType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case SUBJECT_LOCALITY: + StaxParserUtil.advance(xmlEventReader); + + SubjectLocalityType subjectLocalityType = new SubjectLocalityType(); + subjectLocalityType.setAddress(StaxParserUtil.getAttributeValue(elementDetail, SAMLAssertionQNames.ATTR_ADDRESS)); + subjectLocalityType.setDNSName(StaxParserUtil.getAttributeValue(elementDetail, SAMLAssertionQNames.ATTR_DNS_NAME)); + + target.setSubjectLocality(subjectLocalityType); + break; + + case AUTHN_CONTEXT: + target.setAuthnContext(SAMLAuthnContextParser.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/assertion/SAMLConditionsParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLConditionsParser.java new file mode 100755 index 0000000000..3024bc2cc4 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLConditionsParser.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.assertion; + +import org.keycloak.dom.saml.v2.assertion.AudienceRestrictionType; +import org.keycloak.dom.saml.v2.assertion.ConditionsType; +import org.keycloak.dom.saml.v2.assertion.OneTimeUseType; +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; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLConditionsParser extends AbstractStaxSamlAssertionParser { + + private static final SAMLConditionsParser INSTANCE = new SAMLConditionsParser(); + + private SAMLConditionsParser() { + super(SAMLAssertionQNames.CONDITIONS); + } + + public static SAMLConditionsParser getInstance() { + return INSTANCE; + } + + @Override + protected ConditionsType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final ConditionsType conditions = new ConditionsType(); + + conditions.setNotBefore(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLAssertionQNames.ATTR_NOT_BEFORE)); + conditions.setNotOnOrAfter(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLAssertionQNames.ATTR_NOT_ON_OR_AFTER)); + + return conditions; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, ConditionsType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case AUDIENCE_RESTRICTION: + AudienceRestrictionType audienceRestriction = SAMLAudienceRestrictionParser.getInstance().parse(xmlEventReader); + target.addCondition(audienceRestriction); + break; + + case ONE_TIME_USE: + OneTimeUseType oneTimeUseCondition = new OneTimeUseType(); + target.addCondition(oneTimeUseCondition); + 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/AbstractDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLEncryptedAssertionParser.java old mode 100755 new mode 100644 similarity index 50% rename from saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractDescriptorParser.java rename to saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLEncryptedAssertionParser.java index b80d4b0004..c75ec07e71 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractDescriptorParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLEncryptedAssertionParser.java @@ -14,28 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.keycloak.saml.processing.core.parsers.saml.assertion; -package org.keycloak.saml.processing.core.parsers.saml.metadata; - +import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType; import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; - +import org.keycloak.saml.common.parsers.StaxParser; +import org.keycloak.saml.common.util.StaxParserUtil; import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -/** - *

Abstract entity descriptor parser, which provides common parser functionality

- * - * @author Marek Posolda - */ -public abstract class AbstractDescriptorParser extends AbstractParser { +public class SAMLEncryptedAssertionParser implements StaxParser { - protected XMLEventReader filterWhiteSpaceCharacters(XMLEventReader xmlEventReader) throws ParsingException { - try { - return filterWhitespaces(xmlEventReader); - } catch (XMLStreamException e) { - throw new ParsingException(e); - } + private static final SAMLEncryptedAssertionParser INSTANCE = new SAMLEncryptedAssertionParser(); + + public static SAMLEncryptedAssertionParser getInstance() { + return INSTANCE; } + @Override + public EncryptedAssertionType parse(XMLEventReader xmlEventReader) throws ParsingException { + EncryptedAssertionType res = new EncryptedAssertionType(); + res.setEncryptedElement(StaxParserUtil.getDOMElement(xmlEventReader)); + return res; + } } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectConfirmationDataParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectConfirmationDataParser.java new file mode 100644 index 0000000000..5520921e27 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectConfirmationDataParser.java @@ -0,0 +1,68 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationDataType; +import org.keycloak.dom.xmlsec.w3.xmldsig.KeyInfoType; +import org.keycloak.saml.common.constants.WSTrustConstants; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.saml.xmldsig.KeyInfoParser; +import java.util.Objects; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +public class SAMLSubjectConfirmationDataParser extends AbstractStaxSamlAssertionParser { + + public static final SAMLSubjectConfirmationDataParser INSTANCE = new SAMLSubjectConfirmationDataParser(); + + public SAMLSubjectConfirmationDataParser() { + super(SAMLAssertionQNames.SUBJECT_CONFIRMATION_DATA); + } + + @Override + protected SubjectConfirmationDataType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final SubjectConfirmationDataType subjectConfirmationData = new SubjectConfirmationDataType(); + + subjectConfirmationData.setInResponseTo(StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_IN_RESPONSE_TO)); + subjectConfirmationData.setNotBefore(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLAssertionQNames.ATTR_NOT_BEFORE)); + subjectConfirmationData.setNotOnOrAfter(StaxParserUtil.getXmlTimeAttributeValue(element, SAMLAssertionQNames.ATTR_NOT_ON_OR_AFTER)); + subjectConfirmationData.setRecipient(StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_RECIPIENT)); + subjectConfirmationData.setAddress(StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_ADDRESS)); + + return subjectConfirmationData; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, SubjectConfirmationDataType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case KEY_INFO: + KeyInfoType keyInfo = KeyInfoParser.getInstance().parse(xmlEventReader); + target.setAnyType(keyInfo); + break; + + default: + String tag = StaxParserUtil.getElementName(elementDetail); + + if (Objects.equals(tag, WSTrustConstants.XMLEnc.ENCRYPTED_KEY)) { + target.setAnyType(StaxParserUtil.getDOMElement(xmlEventReader)); + } else { + 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/assertion/SAMLSubjectConfirmationParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectConfirmationParser.java new file mode 100644 index 0000000000..1bf94e7554 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectConfirmationParser.java @@ -0,0 +1,71 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.EncryptedElementType; +import org.keycloak.dom.saml.v2.assertion.NameIDType; +import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationDataType; +import org.keycloak.dom.saml.v2.assertion.SubjectConfirmationType; +import org.keycloak.saml.common.ErrorCodes; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.parsers.StaxParser; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import org.w3c.dom.Element; + +public class SAMLSubjectConfirmationParser extends AbstractStaxSamlAssertionParser implements StaxParser { + + public static final SAMLSubjectConfirmationParser INSTANCE = new SAMLSubjectConfirmationParser(); + + public SAMLSubjectConfirmationParser() { + super(SAMLAssertionQNames.SUBJECT_CONFIRMATION); + } + + @Override + protected SubjectConfirmationType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final SubjectConfirmationType res = new SubjectConfirmationType(); + + res.setMethod(StaxParserUtil.getAttributeValue(element, SAMLAssertionQNames.ATTR_METHOD)); + + return res; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, SubjectConfirmationType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case NAMEID: + NameIDType nameID = SAMLParserUtil.parseNameIDType(xmlEventReader); + target.setNameID(nameID); + break; + + case ENCRYPTED_ID: + Element domElement = StaxParserUtil.getDOMElement(xmlEventReader); + target.setEncryptedID(new EncryptedElementType(domElement)); + break; + + case SUBJECT_CONFIRMATION_DATA: + SubjectConfirmationDataType subjectConfirmationData = SAMLSubjectConfirmationDataParser.INSTANCE.parse(xmlEventReader); + target.setSubjectConfirmationData(subjectConfirmationData); + 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/assertion/SAMLSubjectParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectParser.java new file mode 100755 index 0000000000..bce615dca1 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLSubjectParser.java @@ -0,0 +1,79 @@ +/* + * 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.assertion; + +import org.keycloak.dom.saml.v2.assertion.EncryptedElementType; +import org.keycloak.dom.saml.v2.assertion.NameIDType; +import org.keycloak.dom.saml.v2.assertion.SubjectType; +import org.keycloak.saml.common.ErrorCodes; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.parsers.StaxParser; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import org.w3c.dom.Element; + +/** + * Parse the saml subject + * + * @since Oct 12, 2010 + */ +public class SAMLSubjectParser extends AbstractStaxSamlAssertionParser implements StaxParser { + + private static final SAMLSubjectParser INSTANCE = new SAMLSubjectParser(); + + private SAMLSubjectParser() { + super(SAMLAssertionQNames.SUBJECT); + } + + public static SAMLSubjectParser getInstance() { + return INSTANCE; + } + + @Override + protected SubjectType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new SubjectType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, SubjectType target, SAMLAssertionQNames element, StartElement elementDetail) throws ParsingException { + SubjectType.STSubType subType; + switch (element) { + case NAMEID: + NameIDType nameID = SAMLParserUtil.parseNameIDType(xmlEventReader); + subType = new SubjectType.STSubType(); + subType.addBaseID(nameID); + target.setSubType(subType); + break; + + case ENCRYPTED_ID: + Element domElement = StaxParserUtil.getDOMElement(xmlEventReader); + subType = new SubjectType.STSubType(); + subType.setEncryptedID(new EncryptedElementType(domElement)); + target.setSubType(subType); + break; + + case SUBJECT_CONFIRMATION: + target.addConfirmation(SAMLSubjectConfirmationParser.INSTANCE.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/AbstractStaxSamlParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlParser.java new file mode 100644 index 0000000000..c43754c445 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/AbstractStaxSamlParser.java @@ -0,0 +1,50 @@ +/* + * 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/SAMLEntitiesDescriptorParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntitiesDescriptorParser.java index 8df0d3ba42..83e9179986 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 @@ -23,7 +23,7 @@ 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.ParserNamespaceSupport; +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; @@ -34,6 +34,7 @@ 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 @@ -41,16 +42,14 @@ import javax.xml.stream.events.XMLEvent; * @author Anil.Saldhana@redhat.com * @since Jan 31, 2011 */ -public class SAMLEntitiesDescriptorParser extends AbstractDescriptorParser implements ParserNamespaceSupport { +public class SAMLEntitiesDescriptorParser extends AbstractParser { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - private final String EDT = JBossSAMLConstants.ENTITIES_DESCRIPTOR.get(); + private static final String EDT = JBossSAMLConstants.ENTITIES_DESCRIPTOR.get(); public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - xmlEventReader = filterWhiteSpaceCharacters(xmlEventReader); - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, EDT); @@ -93,7 +92,7 @@ public class SAMLEntitiesDescriptorParser extends AbstractDescriptorParser imple if (JBossSAMLConstants.ENTITY_DESCRIPTOR.get().equals(localPart)) { SAMLEntityDescriptorParser entityParser = new SAMLEntityDescriptorParser(); entitiesDescriptorType.addEntityDescriptor(entityParser.parse(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS.get().equalsIgnoreCase(localPart)) { + } else if (JBossSAMLConstants.EXTENSIONS__METADATA.get().equalsIgnoreCase(localPart)) { entitiesDescriptorType.setExtensions(parseExtensions(xmlEventReader)); } else if (JBossSAMLConstants.ENTITIES_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { SAMLEntitiesDescriptorParser parser = new SAMLEntitiesDescriptorParser(); @@ -106,12 +105,6 @@ public class SAMLEntitiesDescriptorParser extends AbstractDescriptorParser imple return entitiesDescriptorType; } - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.ASSERTION_NSURI.get()) && localPart.equals(EDT); - } private ExtensionsType parseExtensions(XMLEventReader xmlEventReader) throws ParsingException { ExtensionsType extensions = new ExtensionsType(); 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 31dfc5f642..f94027db61 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 @@ -31,7 +31,6 @@ 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.RequestedAttributeType; import org.keycloak.dom.saml.v2.metadata.RoleDescriptorType; import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType; import org.keycloak.dom.xmlsec.w3.xmlenc.EncryptionMethodType; @@ -41,9 +40,10 @@ 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.ParserNamespaceSupport; +import org.keycloak.saml.common.parsers.AbstractParser; import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +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; @@ -56,7 +56,10 @@ 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; /** * Parse the SAML Metadata element "EntityDescriptor" @@ -64,16 +67,14 @@ import java.util.List; * @author Anil.Saldhana@redhat.com * @since Dec 14, 2010 */ -public class SAMLEntityDescriptorParser extends AbstractDescriptorParser implements ParserNamespaceSupport { +public class SAMLEntityDescriptorParser extends AbstractParser { private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - private final String EDT = JBossSAMLConstants.ENTITY_DESCRIPTOR.get(); + private static final String EDT = JBossSAMLConstants.ENTITY_DESCRIPTOR.get(); public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - xmlEventReader = filterWhiteSpaceCharacters(xmlEventReader); - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, EDT); @@ -142,8 +143,8 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme entityDescriptorType.addContactPerson(parseContactPerson(xmlEventReader)); } else if (JBossSAMLConstants.ADDITIONAL_METADATA_LOCATION.get().equals(localPart)) { throw logger.unsupportedType("AdditionalMetadataLocation"); - } else if (JBossSAMLConstants.EXTENSIONS.get().equalsIgnoreCase(localPart)) { - entityDescriptorType.setExtensions(parseExtensions(xmlEventReader)); + } 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); @@ -157,19 +158,12 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme return entityDescriptorType; } - public boolean supports(QName qname) { - String nsURI = qname.getNamespaceURI(); - String localPart = qname.getLocalPart(); - - return nsURI.equals(JBossSAMLURIConstants.ASSERTION_NSURI.get()) - && localPart.equals(JBossSAMLConstants.ENTITY_DESCRIPTOR.get()); - } private SPSSODescriptorType parseSPSSODescriptor(XMLEventReader xmlEventReader) throws ParsingException { StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, JBossSAMLConstants.SP_SSO_DESCRIPTOR.get()); - List protocolEnum = SAMLParserUtil.parseProtocolEnumeration(startElement); + List protocolEnum = parseProtocolEnumeration(startElement); SPSSODescriptorType spSSODescriptor = new SPSSODescriptorType(protocolEnum); Attribute wantAssertionsSigned = startElement.getAttributeByName(new QName(JBossSAMLConstants.WANT_ASSERTIONS_SIGNED @@ -224,7 +218,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme spSSODescriptor.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); } else if (JBossSAMLConstants.KEY_DESCRIPTOR.get().equalsIgnoreCase(localPart)) { spSSODescriptor.addKeyDescriptor(parseKeyDescriptor(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS.get().equalsIgnoreCase(localPart)) { + } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { spSSODescriptor.setExtensions(parseExtensions(xmlEventReader)); } else throw logger.parserUnknownTag(localPart, startElement.getLocation()); @@ -236,7 +230,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, JBossSAMLConstants.IDP_SSO_DESCRIPTOR.get()); - List protocolEnum = SAMLParserUtil.parseProtocolEnumeration(startElement); + List protocolEnum = parseProtocolEnumeration(startElement); IDPSSODescriptorType idpSSODescriptor = new IDPSSODescriptorType(protocolEnum); Attribute wantAuthnSigned = startElement.getAttributeByName(new QName(JBossSAMLConstants.WANT_AUTHN_REQUESTS_SIGNED @@ -304,11 +298,11 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme startElement = StaxParserUtil.getNextStartElement(xmlEventReader); idpSSODescriptor.addNameIDFormat(StaxParserUtil.getElementText(xmlEventReader)); } else if (JBossSAMLConstants.ATTRIBUTE.get().equalsIgnoreCase(localPart)) { - AttributeType attribute = SAMLParserUtil.parseAttribute(xmlEventReader); + 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.get().equalsIgnoreCase(localPart)) { + } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { idpSSODescriptor.setExtensions(parseExtensions(xmlEventReader)); } else throw logger.parserUnknownTag(localPart, startElement.getLocation()); @@ -335,7 +329,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme throws ParsingException { StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, JBossSAMLConstants.ATTRIBUTE_AUTHORITY_DESCRIPTOR.get()); - List protocolEnum = SAMLParserUtil.parseProtocolEnumeration(startElement); + List protocolEnum = parseProtocolEnumeration(startElement); AttributeAuthorityDescriptorType attributeAuthority = new AttributeAuthorityDescriptorType(protocolEnum); while (xmlEventReader.hasNext()) { @@ -363,17 +357,25 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme 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(SAMLParserUtil.parseAttribute(xmlEventReader)); + 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.get().equalsIgnoreCase(localPart)) { + } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { attributeAuthority.setExtensions(parseExtensions(xmlEventReader)); } else throw logger.parserUnknownTag(localPart, startElement.getLocation()); @@ -415,7 +417,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme LocalizedURIType localName = new LocalizedURIType(langVal); localName.setValue(URI.create(StaxParserUtil.getElementText(xmlEventReader))); org.addOrganizationURL(localName); - } else if (JBossSAMLConstants.EXTENSIONS.get().equalsIgnoreCase(localPart)) { + } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { org.setExtensions(parseExtensions(xmlEventReader)); } else throw logger.parserUnknownTag(localPart, startElement.getLocation()); @@ -531,7 +533,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme } else if (JBossSAMLConstants.TELEPHONE_NUMBER.get().equals(localPart)) { startElement = StaxParserUtil.getNextStartElement(xmlEventReader); contactType.addTelephone(StaxParserUtil.getElementText(xmlEventReader)); - } else if (JBossSAMLConstants.EXTENSIONS.get().equalsIgnoreCase(localPart)) { + } else if (JBossSAMLConstants.EXTENSIONS__PROTOCOL.get().equalsIgnoreCase(localPart)) { contactType.setExtensions(parseExtensions(xmlEventReader)); } else throw logger.parserUnknownTag(localPart, startElement.getLocation()); @@ -619,8 +621,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme LocalizedNameType localName = getLocalizedName(xmlEventReader, startElement); attributeConsumer.addServiceDescription(localName); } else if (JBossSAMLConstants.REQUESTED_ATTRIBUTE.get().equals(localPart)) { - RequestedAttributeType attType = parseRequestedAttributeType(xmlEventReader, startElement); - attributeConsumer.addRequestedAttribute(attType); + attributeConsumer.addRequestedAttribute(SAMLRequestedAttributeParser.getInstance().parse(xmlEventReader)); } else throw logger.parserUnknownTag(localPart, startElement.getLocation()); } @@ -628,27 +629,6 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme return attributeConsumer; } - private RequestedAttributeType parseRequestedAttributeType(XMLEventReader xmlEventReader, StartElement startElement) - throws ParsingException { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.REQUESTED_ATTRIBUTE.get()); - RequestedAttributeType attributeType = null; - - Attribute name = startElement.getAttributeByName(new QName(JBossSAMLConstants.NAME.get())); - if (name == null) - throw logger.parserRequiredAttribute("Name"); - attributeType = new RequestedAttributeType(StaxParserUtil.getAttributeValue(name)); - - Attribute isRequired = startElement.getAttributeByName(new QName(JBossSAMLConstants.IS_REQUIRED.get())); - if (isRequired != null) { - attributeType.setIsRequired(Boolean.parseBoolean(StaxParserUtil.getAttributeValue(isRequired))); - } - - SAMLParserUtil.parseAttributeType(xmlEventReader, startElement, JBossSAMLConstants.REQUESTED_ATTRIBUTE.get(), - attributeType); - return attributeType; - } - private ExtensionsType parseExtensions(XMLEventReader xmlEventReader) throws ParsingException { ExtensionsType extensions = new ExtensionsType(); Element extElement = StaxParserUtil.getDOMElement(xmlEventReader); @@ -659,7 +639,7 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme private RoleDescriptorType parseRoleDescriptor(XMLEventReader xmlEventReader) throws ParsingException { StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, JBossSAMLConstants.ROLE_DESCRIPTOR.get()); - List protocolEnum = SAMLParserUtil.parseProtocolEnumeration(startElement); + List protocolEnum = parseProtocolEnumeration(startElement); RoleDescriptorType roleDescriptorType = new RoleDescriptorType(protocolEnum) {}; while (xmlEventReader.hasNext()) { @@ -683,4 +663,25 @@ public class SAMLEntityDescriptorParser extends AbstractDescriptorParser impleme 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()); + } + + } + 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 new file mode 100644 index 0000000000..585c88f1f2 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLExtensionsParser.java @@ -0,0 +1,52 @@ +/* + * 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 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; + +/** + * Parses <samlp:Extensions> SAML2 element into series of DOM nodes. + * + * @author hmlnarik + */ +public class SAMLExtensionsParser extends AbstractStaxSamlParser { + + private static final SAMLExtensionsParser INSTANCE = new SAMLExtensionsParser(); + + private SAMLExtensionsParser() { + super(JBossSAMLConstants.EXTENSIONS__METADATA); + } + + public static SAMLExtensionsParser getInstance() { + return INSTANCE; + } + + @Override + protected ExtensionsType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new ExtensionsType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, ExtensionsType target, JBossSAMLConstants element, StartElement elementDetail) throws ParsingException { + target.setElement(StaxParserUtil.getDOMElement(xmlEventReader)); + } +} 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 new file mode 100644 index 0000000000..b1dbb7d454 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLRequestedAttributeParser.java @@ -0,0 +1,80 @@ +/* + * 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.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; + +/** + * Parse the in the saml assertion + * + * @since Oct 14, 2010 + */ +public class SAMLRequestedAttributeParser extends AbstractStaxSamlParser { + + 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); + } + + public static SAMLRequestedAttributeParser getInstance() { + return INSTANCE; + } + + @Override + protected RequestedAttributeType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + RequestedAttributeType attributeType; + + // TODO: replace all constants with SamlMetadataQNames ones + attributeType = new RequestedAttributeType(StaxParserUtil.getRequiredAttributeValue(element, SAMLAssertionQNames.ATTR_NAME)); + attributeType.setFriendlyName(StaxParserUtil.getAttributeValue(element, JBossSAMLConstants.FRIENDLY_NAME.get())); + attributeType.setIsRequired(StaxParserUtil.getBooleanAttributeValue(element, JBossSAMLConstants.IS_REQUIRED.get())); + attributeType.setNameFormat(StaxParserUtil.getAttributeValue(element, JBossSAMLConstants.NAME_FORMAT.get())); + + Attribute x500EncodingAttr = element.getAttributeByName(X500_ENCODING); + if (x500EncodingAttr != null) { + attributeType.getOtherAttributes().put(x500EncodingAttr.getName(), StaxParserUtil.getAttributeValue(x500EncodingAttr)); + } + + return attributeType; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, RequestedAttributeType target, JBossSAMLConstants 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/protocol/AbstractStaxSamlProtocolParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/AbstractStaxSamlProtocolParser.java new file mode 100644 index 0000000000..5aacd8a107 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/AbstractStaxSamlProtocolParser.java @@ -0,0 +1,40 @@ +/* + * 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.protocol; + +import org.keycloak.saml.common.parsers.AbstractStaxParser; +import org.keycloak.saml.processing.core.parsers.util.QNameEnumLookup; +import javax.xml.namespace.QName; + +/** + * + * @author hmlnarik + */ +public abstract class AbstractStaxSamlProtocolParser extends AbstractStaxParser { + + protected static final QNameEnumLookup LOOKUP = new QNameEnumLookup(SAMLProtocolQNames.values()); + + public AbstractStaxSamlProtocolParser(SAMLProtocolQNames expectedStartElement) { + super(expectedStartElement.getQName(), SAMLProtocolQNames.UNKNOWN_ELEMENT); + } + + @Override + protected SAMLProtocolQNames getElementFromName(QName name) { + return LOOKUP.from(name); + } + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResolveParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResolveParser.java new file mode 100755 index 0000000000..00271d0c0f --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResolveParser.java @@ -0,0 +1,87 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.ArtifactResolveType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import static org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLRequestAbstractParser.VERSION_2_0; + +/** + * Parse the {@link ArtifactResolveType} + * + * @since Jul 1, 2011 + */ +public class SAMLArtifactResolveParser extends SAMLRequestAbstractParser { + + private static final SAMLArtifactResolveParser INSTANCE = new SAMLArtifactResolveParser(); + + private SAMLArtifactResolveParser() { + super(SAMLProtocolQNames.ARTIFACT_RESOLVE); + } + + public static SAMLArtifactResolveParser getInstance() { + return INSTANCE; + } + + /** + * Parse the attributes at the authnrequesttype element + * + * @param startElement + * + * @return + * + * @throws ParsingException + */ + @Override + protected ArtifactResolveType instantiateElement(XMLEventReader xmlEventReader, StartElement startElement) throws ParsingException { + SAMLParserUtil.validateAttributeValue(startElement, SAMLProtocolQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(startElement, SAMLProtocolQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(startElement, SAMLProtocolQNames.ATTR_ISSUE_INSTANT)); + + ArtifactResolveType authnRequest = new ArtifactResolveType(id, issueInstant); + super.parseBaseAttributes(startElement, authnRequest); + + return authnRequest; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, ArtifactResolveType target, + SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + case SIGNATURE: + case EXTENSIONS: + parseCommonElements(element, elementDetail, xmlEventReader, target); + break; + + case ARTIFACT: + StaxParserUtil.advance(xmlEventReader); + target.setArtifact(StaxParserUtil.getElementText(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/protocol/SAMLArtifactResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResponseParser.java new file mode 100755 index 0000000000..663e37265e --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResponseParser.java @@ -0,0 +1,97 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.ArtifactResponseType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import org.w3c.dom.Element; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import static org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLStatusResponseTypeParser.VERSION_2_0; + +/** + * Parse the SAML Response + * + * @since July 1, 2011 + */ +public class SAMLArtifactResponseParser extends SAMLStatusResponseTypeParser { + + private static final SAMLArtifactResponseParser INSTANCE = new SAMLArtifactResponseParser(); + + private SAMLArtifactResponseParser() { + super(SAMLProtocolQNames.ARTIFACT_RESPONSE); + } + + public static SAMLArtifactResponseParser getInstance() { + return INSTANCE; + } + + @Override + protected ArtifactResponseType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + SAMLParserUtil.validateAttributeValue(element, SAMLProtocolQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ISSUE_INSTANT)); + + ArtifactResponseType res = new ArtifactResponseType(id, issueInstant); + + // Let us set the attributes + super.parseBaseAttributes(element, res); + + return res; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, ArtifactResponseType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + target.setIssuer(SAMLParserUtil.parseNameIDType(xmlEventReader)); + break; + + case SIGNATURE: + Element sig = StaxParserUtil.getDOMElement(xmlEventReader); + target.setSignature(sig); + break; + + case EXTENSIONS: + SAMLExtensionsParser extensionsParser = SAMLExtensionsParser.getInstance(); + target.setExtensions(extensionsParser.parse(xmlEventReader)); + break; + + case AUTHN_REQUEST: + SAMLAuthNRequestParser authnParser = SAMLAuthNRequestParser.getInstance(); + target.setAny(authnParser.parse(xmlEventReader)); + break; + + case RESPONSE: + SAMLResponseParser responseParser = SAMLResponseParser.getInstance(); + target.setAny(responseParser.parse(xmlEventReader)); + break; + + case STATUS: + target.setStatus(SAMLStatusParser.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/protocol/SAMLAttributeQueryParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLAttributeQueryParser.java new file mode 100755 index 0000000000..c255cb34f9 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLAttributeQueryParser.java @@ -0,0 +1,83 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.AttributeQueryType; +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 org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeParser; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLSubjectParser; +import org.keycloak.saml.common.parsers.StaxParser; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import static org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLRequestAbstractParser.VERSION_2_0; + +/** + * Parse the {@link org.keycloak.dom.saml.v2.protocol.ArtifactResolveType} + * + * @since Jul 1, 2011 + */ +public class SAMLAttributeQueryParser extends SAMLRequestAbstractParser implements StaxParser { + + private static final SAMLAttributeQueryParser INSTANCE = new SAMLAttributeQueryParser(); + + private SAMLAttributeQueryParser() { + super(SAMLProtocolQNames.ATTRIBUTE_QUERY); + } + + public static SAMLAttributeQueryParser getInstance() { + return INSTANCE; + } + + @Override + protected AttributeQueryType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + SAMLParserUtil.validateAttributeValue(element, SAMLProtocolQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ISSUE_INSTANT)); + + AttributeQueryType authnRequest = new AttributeQueryType(id, issueInstant); + super.parseBaseAttributes(element, authnRequest); + + return authnRequest; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AttributeQueryType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + case SIGNATURE: + case EXTENSIONS: + parseCommonElements(element, elementDetail, xmlEventReader, target); + break; + + case SUBJECT: + target.setSubject(SAMLSubjectParser.getInstance().parse(xmlEventReader)); + break; + + case ATTRIBUTE: + target.add(SAMLAttributeParser.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/protocol/SAMLAuthNRequestParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLAuthNRequestParser.java new file mode 100755 index 0000000000..592cc503ff --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLAuthNRequestParser.java @@ -0,0 +1,127 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; +import org.keycloak.dom.saml.v2.protocol.NameIDPolicyType; +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 org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLConditionsParser; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLSubjectParser; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import static org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLRequestAbstractParser.VERSION_2_0; + +/** + * Parse the SAML2 AuthnRequest + * + * @since Nov 2, 2010 + */ +public class SAMLAuthNRequestParser extends SAMLRequestAbstractParser { + + private static final SAMLAuthNRequestParser INSTANCE = new SAMLAuthNRequestParser(); + + private SAMLAuthNRequestParser() { + super(SAMLProtocolQNames.AUTHN_REQUEST); + } + + public static SAMLAuthNRequestParser getInstance() { + return INSTANCE; + } + + /** + * Parse the attributes at the authnrequesttype element + * + * @param startElement + * + * @return + * + * @throws ParsingException + */ + @Override + protected AuthnRequestType instantiateElement(XMLEventReader xmlEventReader, StartElement startElement) throws ParsingException { + SAMLParserUtil.validateAttributeValue(startElement, SAMLProtocolQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(startElement, SAMLProtocolQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(startElement, SAMLProtocolQNames.ATTR_ISSUE_INSTANT)); + + AuthnRequestType authnRequest = new AuthnRequestType(id, issueInstant); + super.parseBaseAttributes(startElement, authnRequest); + + authnRequest.setAssertionConsumerServiceURL(StaxParserUtil.getUriAttributeValue(startElement, SAMLProtocolQNames.ATTR_ASSERTION_CONSUMER_SERVICE_URL)); + authnRequest.setAssertionConsumerServiceIndex(StaxParserUtil.getIntegerAttributeValue(startElement, SAMLProtocolQNames.ATTR_ASSERTION_CONSUMER_SERVICE_INDEX)); + authnRequest.setAttributeConsumingServiceIndex(StaxParserUtil.getIntegerAttributeValue(startElement, SAMLProtocolQNames.ATTR_ATTRIBUTE_CONSUMING_SERVICE_INDEX)); + authnRequest.setForceAuthn(StaxParserUtil.getBooleanAttributeValue(startElement, SAMLProtocolQNames.ATTR_FORCE_AUTHN)); + authnRequest.setIsPassive(StaxParserUtil.getBooleanAttributeValue(startElement, SAMLProtocolQNames.ATTR_IS_PASSIVE)); + authnRequest.setProtocolBinding(StaxParserUtil.getUriAttributeValue(startElement, SAMLProtocolQNames.ATTR_PROTOCOL_BINDING)); + authnRequest.setProviderName(StaxParserUtil.getAttributeValue(startElement, SAMLProtocolQNames.ATTR_PROVIDER_NAME)); + + return authnRequest; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, AuthnRequestType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + case SIGNATURE: + case EXTENSIONS: + parseCommonElements(element, elementDetail, xmlEventReader, target); + break; + + case NAMEID_POLICY: + StaxParserUtil.advance(xmlEventReader); + target.setNameIDPolicy(getNameIDPolicy(elementDetail)); + break; + + case SUBJECT: + target.setSubject(SAMLSubjectParser.getInstance().parse(xmlEventReader)); + break; + + case CONDITIONS: + target.setConditions(SAMLConditionsParser.getInstance().parse(xmlEventReader)); + break; + + case REQUESTED_AUTHN_CONTEXT: + target.setRequestedAuthnContext(SAMLRequestedAuthnContextParser.getInstance().parse(xmlEventReader)); + break; + + case SCOPING: + StaxParserUtil.bypassElementBlock(xmlEventReader, element.getQName()); + break; + + default: + throw LOGGER.parserUnknownTag(StaxParserUtil.getElementName(elementDetail), elementDetail.getLocation()); + } + } + + /** + * Get the NameIDPolicy + * + * @param startElement + * + * @return + */ + private NameIDPolicyType getNameIDPolicy(StartElement startElement) { + NameIDPolicyType nameIDPolicy = new NameIDPolicyType(); + nameIDPolicy.setFormat(StaxParserUtil.getUriAttributeValue(startElement, SAMLProtocolQNames.ATTR_FORMAT)); + nameIDPolicy.setAllowCreate(StaxParserUtil.getBooleanAttributeValue(startElement, SAMLProtocolQNames.ATTR_ALLOW_CREATE)); + return nameIDPolicy; + } +} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLExtensionsParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLExtensionsParser.java new file mode 100644 index 0000000000..0a72ddac9d --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLExtensionsParser.java @@ -0,0 +1,51 @@ +/* + * 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.protocol; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +/** + * Parses <samlp:Extensions> SAML2 element into series of DOM nodes. + * + * @author hmlnarik + */ +public class SAMLExtensionsParser extends AbstractStaxSamlProtocolParser { + + private static final SAMLExtensionsParser INSTANCE = new SAMLExtensionsParser(); + + private SAMLExtensionsParser() { + super(SAMLProtocolQNames.EXTENSIONS); + } + + public static SAMLExtensionsParser getInstance() { + return INSTANCE; + } + + @Override + protected ExtensionsType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new ExtensionsType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, ExtensionsType target, SAMLProtocolQNames 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/protocol/SAMLProtocolQNames.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLProtocolQNames.java new file mode 100644 index 0000000000..6af0a3c1ac --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLProtocolQNames.java @@ -0,0 +1,125 @@ +/* + * 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.protocol; + +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 javax.xml.namespace.QName; +import org.keycloak.saml.processing.core.parsers.util.HasQName; + +/** + * Elements from saml-schema-protocol-2.0.xsd + * @author hmlnarik + */ +public enum SAMLProtocolQNames implements HasQName { + + ARTIFACT("Artifact"), + ARTIFACT_RESOLVE("ArtifactResolve"), + ARTIFACT_RESPONSE("ArtifactResponse"), + ASSERTION_ID_REQUEST("AssertionIDRequest"), + ATTRIBUTE_QUERY("AttributeQuery"), + AUTHN_QUERY("AuthnQuery"), + AUTHN_REQUEST("AuthnRequest"), + AUTHZ_DECISION_QUERY("AuthzDecisionQuery"), + EXTENSIONS("Extensions"), + GET_COMPLETE("GetComplete"), + IDP_ENTRY("IDPEntry"), + IDP_LIST("IDPList"), + LOGOUT_REQUEST("LogoutRequest"), + LOGOUT_RESPONSE("LogoutResponse"), + MANAGE_NAMEID_REQUEST("ManageNameIDRequest"), + MANAGE_NAMEID_RESPONSE("ManageNameIDResponse"), + NAMEID_MAPPING_REQUEST("NameIDMappingRequest"), + NAMEID_MAPPING_RESPONSE("NameIDMappingResponse"), + NAMEID_POLICY("NameIDPolicy"), + NEW_ENCRYPTEDID("NewEncryptedID"), + NEWID("NewID"), + REQUESTED_AUTHN_CONTEXT("RequestedAuthnContext"), + REQUESTERID("RequesterID"), + RESPONSE("Response"), + SCOPING("Scoping"), + SESSION_INDEX("SessionIndex"), + STATUS_CODE("StatusCode"), + STATUS_DETAIL("StatusDetail"), + STATUS_MESSAGE("StatusMessage"), + STATUS("Status"), + SUBJECT_QUERY("SubjectQuery"), + TERMINATE("Terminate"), + + // Attribute names + ATTR_ALLOW_CREATE(null, "AllowCreate"), + ATTR_ASSERTION_CONSUMER_SERVICE_URL(null, "AssertionConsumerServiceURL"), + ATTR_ASSERTION_CONSUMER_SERVICE_INDEX(null, "AssertionConsumerServiceIndex"), + ATTR_ATTRIBUTE_CONSUMING_SERVICE_INDEX(null, "AttributeConsumingServiceIndex"), + ATTR_COMPARISON(null, "Comparison"), + ATTR_CONSENT(null, "Consent"), + ATTR_DESTINATION(null, "Destination"), + ATTR_FORCE_AUTHN(null, "ForceAuthn"), + ATTR_FORMAT(null, "Format"), + ATTR_ID(null, "ID"), + ATTR_IN_RESPONSE_TO(null, "InResponseTo"), + ATTR_IS_PASSIVE(null, "IsPassive"), + ATTR_ISSUE_INSTANT(null, "IssueInstant"), + ATTR_NOT_BEFORE(null, "NotBefore"), + ATTR_NOT_ON_OR_AFTER(null, "NotOnOrAfter"), + ATTR_PROTOCOL_BINDING(null, "ProtocolBinding"), + ATTR_PROVIDER_NAME(null, "ProviderName"), + ATTR_REASON(null, "Reason"), + ATTR_VALUE(null, "Value"), + ATTR_VERSION(null, "Version"), + + // Elements from other namespaces that can be direct subelements of this namespace's elements + ATTRIBUTE(SAMLAssertionQNames.ATTRIBUTE), + ASSERTION(SAMLAssertionQNames.ASSERTION), + AUTHN_CONTEXT_CLASS_REF(SAMLAssertionQNames.AUTHN_CONTEXT_CLASS_REF), + AUTHN_CONTEXT_DECL_REF(SAMLAssertionQNames.AUTHN_CONTEXT_DECL_REF), + BASEID(SAMLAssertionQNames.BASEID), + CONDITIONS(SAMLAssertionQNames.CONDITIONS), + ENCRYPTED_ASSERTION(SAMLAssertionQNames.ENCRYPTED_ASSERTION), + ISSUER(SAMLAssertionQNames.ISSUER), + NAMEID(SAMLAssertionQNames.NAMEID), + SIGNATURE(XmlDSigQNames.SIGNATURE), + ENCRYPTED_ID(SAMLAssertionQNames.ENCRYPTED_ID), + SUBJECT(SAMLAssertionQNames.SUBJECT), + + UNKNOWN_ELEMENT("") + ; + + private final QName qName; + + private SAMLProtocolQNames(String localName) { + this(JBossSAMLURIConstants.PROTOCOL_NSURI, localName); + } + + private SAMLProtocolQNames(HasQName source) { + this.qName = source.getQName(); + } + + private SAMLProtocolQNames(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/protocol/SAMLRequestAbstractParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLRequestAbstractParser.java new file mode 100755 index 0000000000..a714fff0a5 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLRequestAbstractParser.java @@ -0,0 +1,75 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.RequestAbstractType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * Base Class for SAML Request Parsing + * + * @since Nov 2, 2010 + */ +public abstract class SAMLRequestAbstractParser extends AbstractStaxSamlProtocolParser { + + protected static final String VERSION_2_0 = "2.0"; + + protected SAMLRequestAbstractParser(SAMLProtocolQNames expectedStartElement) { + super(expectedStartElement); + } + + /** + * Parse the attributes that are common to all SAML Request Types + * + * @param startElement + * @param request + * + * @throws ParsingException + */ + protected void parseBaseAttributes(StartElement startElement, T request) throws ParsingException { + request.setDestination(StaxParserUtil.getUriAttributeValue(startElement, SAMLProtocolQNames.ATTR_DESTINATION)); + request.setConsent(StaxParserUtil.getAttributeValue(startElement, SAMLProtocolQNames.ATTR_CONSENT)); + } + + /** + * If the current element is one of the common request elements (Issuer, Signature, Extensions), parses it. + * @param element + * @param xmlEventReader + * @param request + * @throws ParsingException + */ + protected void parseCommonElements(SAMLProtocolQNames element, StartElement elementDetail, XMLEventReader xmlEventReader, RequestAbstractType request) + throws ParsingException { + switch (element) { + case ISSUER: + request.setIssuer(SAMLParserUtil.parseNameIDType(xmlEventReader)); + break; + + case SIGNATURE: + request.setSignature(StaxParserUtil.getDOMElement(xmlEventReader)); + break; + + case EXTENSIONS: + request.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); + break; + } + } +} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLRequestedAuthnContextParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLRequestedAuthnContextParser.java new file mode 100644 index 0000000000..550612b67d --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLRequestedAuthnContextParser.java @@ -0,0 +1,85 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.AuthnContextComparisonType; +import org.keycloak.dom.saml.v2.protocol.RequestedAuthnContextType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.StartElement; + +/** + * Parse the SAML2 RequestedAuthnContext + * + * @since Nov 2, 2010 + */ +public class SAMLRequestedAuthnContextParser extends AbstractStaxSamlProtocolParser { + + private static final SAMLRequestedAuthnContextParser INSTANCE = new SAMLRequestedAuthnContextParser(); + + private SAMLRequestedAuthnContextParser() { + super(SAMLProtocolQNames.REQUESTED_AUTHN_CONTEXT); + } + + public static SAMLRequestedAuthnContextParser getInstance() { + return INSTANCE; + } + + /** + * Parse the attributes at the authnrequesttype element + * + * @param startElement + * + * @return + * + * @throws ParsingException + */ + @Override + protected RequestedAuthnContextType instantiateElement(XMLEventReader xmlEventReader, StartElement startElement) throws ParsingException { + RequestedAuthnContextType context = new RequestedAuthnContextType(); + + Attribute comparison = startElement.getAttributeByName(SAMLProtocolQNames.ATTR_COMPARISON.getQName()); + if (comparison != null) { + context.setComparison(AuthnContextComparisonType.fromValue(comparison.getValue())); + } + + return context; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, RequestedAuthnContextType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case AUTHN_CONTEXT_CLASS_REF: + StaxParserUtil.advance(xmlEventReader); + String value = StaxParserUtil.getElementText(xmlEventReader); + target.addAuthnContextClassRef(value); + break; + + case AUTHN_CONTEXT_DECL_REF: + StaxParserUtil.advance(xmlEventReader); + value = StaxParserUtil.getElementText(xmlEventReader); + target.addAuthnContextDeclRef(value); + 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/protocol/SAMLResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLResponseParser.java new file mode 100755 index 0000000000..5a30e9b371 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLResponseParser.java @@ -0,0 +1,96 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.ResponseType.RTChoiceType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import org.w3c.dom.Element; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionParser; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLEncryptedAssertionParser; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; + +/** + * Parse the SAML Response + * + * @since Nov 2, 2010 + */ +public class SAMLResponseParser extends SAMLStatusResponseTypeParser { + + private static final SAMLResponseParser INSTANCE = new SAMLResponseParser(); + + private SAMLResponseParser() { + super(SAMLProtocolQNames.RESPONSE); + } + + public static SAMLResponseParser getInstance() { + return INSTANCE; + } + + @Override + protected ResponseType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + SAMLParserUtil.validateAttributeValue(element, SAMLProtocolQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ISSUE_INSTANT)); + + ResponseType res = new ResponseType(id, issueInstant); + + // Let us set the attributes + super.parseBaseAttributes(element, res); + + return res; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, ResponseType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + target.setIssuer(SAMLParserUtil.parseNameIDType(xmlEventReader)); + break; + + case SIGNATURE: + Element sig = StaxParserUtil.getDOMElement(xmlEventReader); + target.setSignature(sig); + break; + + case ASSERTION: + target.addAssertion(new RTChoiceType(SAMLAssertionParser.getInstance().parse(xmlEventReader))); + break; + + case EXTENSIONS: + target.setExtensions(SAMLExtensionsParser.getInstance().parse(xmlEventReader)); + break; + + case STATUS: + target.setStatus(SAMLStatusParser.getInstance().parse(xmlEventReader)); + break; + + case ENCRYPTED_ASSERTION: + target.addAssertion(new RTChoiceType(SAMLEncryptedAssertionParser.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/protocol/SAMLSloRequestParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLSloRequestParser.java new file mode 100755 index 0000000000..d1da7cf93b --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLSloRequestParser.java @@ -0,0 +1,94 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.assertion.EncryptedElementType; +import org.keycloak.dom.saml.v2.assertion.NameIDType; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; + +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import org.w3c.dom.Element; +import static org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLRequestAbstractParser.VERSION_2_0; + +/** + * Parse the Single Log Out requests + * + * @since Nov 3, 2010 + */ +public class SAMLSloRequestParser extends SAMLRequestAbstractParser { + + private static final SAMLSloRequestParser INSTANCE = new SAMLSloRequestParser(); + + private SAMLSloRequestParser() { + super(SAMLProtocolQNames.LOGOUT_REQUEST); + } + + public static SAMLSloRequestParser getInstance() { + return INSTANCE; + } + + @Override + protected LogoutRequestType instantiateElement(XMLEventReader xmlEventReader, StartElement startElement) throws ParsingException { + SAMLParserUtil.validateAttributeValue(startElement, SAMLProtocolQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(startElement, SAMLProtocolQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(startElement, SAMLProtocolQNames.ATTR_ISSUE_INSTANT)); + + LogoutRequestType logoutRequest = new LogoutRequestType(id, issueInstant); + super.parseBaseAttributes(startElement, logoutRequest); + + logoutRequest.setReason(StaxParserUtil.getAttributeValue(startElement, SAMLProtocolQNames.ATTR_REASON)); + logoutRequest.setNotOnOrAfter(StaxParserUtil.getXmlTimeAttributeValue(startElement, SAMLProtocolQNames.ATTR_NOT_ON_OR_AFTER)); + + return logoutRequest; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, LogoutRequestType target, + SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + case SIGNATURE: + case EXTENSIONS: + parseCommonElements(element, elementDetail, xmlEventReader, target); + break; + + case NAMEID: + NameIDType nameID = SAMLParserUtil.parseNameIDType(xmlEventReader); + target.setNameID(nameID); + break; + + case ENCRYPTED_ID: + Element domElement = StaxParserUtil.getDOMElement(xmlEventReader); + target.setEncryptedID(new EncryptedElementType(domElement)); + break; + + case SESSION_INDEX: + StaxParserUtil.getNextStartElement(xmlEventReader); + target.addSessionIndex(StaxParserUtil.getElementText(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/protocol/SAMLSloResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLSloResponseParser.java new file mode 100755 index 0000000000..7366a9fee2 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLSloResponseParser.java @@ -0,0 +1,88 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; +import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +import org.w3c.dom.Element; +import static org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLStatusResponseTypeParser.VERSION_2_0; + +/** + * Parse the SLO Response + * + * @since Nov 3, 2010 + */ +public class SAMLSloResponseParser extends SAMLStatusResponseTypeParser { + + private static final SAMLSloResponseParser INSTANCE = new SAMLSloResponseParser(); + + public SAMLSloResponseParser() { + super(SAMLProtocolQNames.LOGOUT_RESPONSE); + } + + public static SAMLSloResponseParser getInstance() { + return INSTANCE; + } + + @Override + protected StatusResponseType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + SAMLParserUtil.validateAttributeValue(element, SAMLProtocolQNames.ATTR_VERSION, VERSION_2_0); + String id = StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ID); + XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getRequiredAttributeValue(element, SAMLProtocolQNames.ATTR_ISSUE_INSTANT)); + + StatusResponseType res = new StatusResponseType(id, issueInstant); + + // Let us set the attributes + super.parseBaseAttributes(element, res); + + return res; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, StatusResponseType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ISSUER: + target.setIssuer(SAMLParserUtil.parseNameIDType(xmlEventReader)); + break; + + case SIGNATURE: + Element sig = StaxParserUtil.getDOMElement(xmlEventReader); + target.setSignature(sig); + break; + + case EXTENSIONS: + SAMLExtensionsParser extensionsParser = SAMLExtensionsParser.getInstance(); + target.setExtensions(extensionsParser.parse(xmlEventReader)); + break; + + case STATUS: + target.setStatus(SAMLStatusParser.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/protocol/SAMLStatusCodeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusCodeParser.java new file mode 100644 index 0000000000..b471381e62 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusCodeParser.java @@ -0,0 +1,59 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.StatusCodeType; +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; + +/** + * Base Class for all Response Type parsing for SAML2 + * + */ +public class SAMLStatusCodeParser extends AbstractStaxSamlProtocolParser { + + private static final SAMLStatusCodeParser INSTANCE = new SAMLStatusCodeParser(); + + private SAMLStatusCodeParser() { + super(SAMLProtocolQNames.STATUS_CODE); + } + + public static SAMLStatusCodeParser getInstance() { + return INSTANCE; + } + + @Override + protected StatusCodeType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final StatusCodeType res = new StatusCodeType(); + res.setValue(StaxParserUtil.getUriAttributeValue(element, SAMLProtocolQNames.ATTR_VALUE)); + return res; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, StatusCodeType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case STATUS_CODE: + target.setStatusCode(SAMLStatusCodeParser.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/protocol/SAMLStatusParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusParser.java new file mode 100644 index 0000000000..c44993cb35 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusParser.java @@ -0,0 +1,78 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.StatusDetailType; +import org.keycloak.dom.saml.v2.protocol.StatusType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.parsers.AnyDomParser; +import org.keycloak.saml.common.util.StaxParserUtil; +import org.w3c.dom.Element; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; +import java.util.List; + +/** + * Base Class for all Response Type parsing for SAML2 + * + */ +public class SAMLStatusParser extends AbstractStaxSamlProtocolParser { + + private static final SAMLStatusParser INSTANCE = new SAMLStatusParser(); + private static final AnyDomParser STATUS_DETAIL_PARSER = AnyDomParser.getInstance(SAMLProtocolQNames.STATUS_DETAIL.getQName()); + + private SAMLStatusParser() { + super(SAMLProtocolQNames.STATUS); + } + + public static SAMLStatusParser getInstance() { + return INSTANCE; + } + + @Override + protected StatusType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new StatusType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, StatusType target, SAMLProtocolQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case STATUS_CODE: + target.setStatusCode(SAMLStatusCodeParser.getInstance().parse(xmlEventReader)); + break; + + case STATUS_MESSAGE: + StaxParserUtil.advance(xmlEventReader); + target.setStatusMessage(StaxParserUtil.getElementText(xmlEventReader)); + break; + + case STATUS_DETAIL: + List elements = STATUS_DETAIL_PARSER.parse(xmlEventReader); + + StatusDetailType statusDetailType = new StatusDetailType(); + for (Element e : elements) { + statusDetailType.addStatusDetail(e); + } + target.setStatusDetail(statusDetailType); + 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/protocol/SAMLStatusResponseTypeParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusResponseTypeParser.java new file mode 100755 index 0000000000..dfc34a72ca --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLStatusResponseTypeParser.java @@ -0,0 +1,49 @@ +/* + * 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.protocol; + +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; +import javax.xml.stream.events.StartElement; + +/** + * Base Class for all Response Type parsing for SAML2 + * + */ +public abstract class SAMLStatusResponseTypeParser extends AbstractStaxSamlProtocolParser { + + protected static final String VERSION_2_0 = "2.0"; + + protected SAMLStatusResponseTypeParser(SAMLProtocolQNames expectedStartElement) { + super(expectedStartElement); + } + + /** + * Parse the attributes that are common to all SAML Response Types + * + * @param startElement + * @param response + * + * @throws org.keycloak.saml.common.exceptions.ParsingException + */ + protected void parseBaseAttributes(StartElement startElement, T response) throws ParsingException { + response.setDestination(StaxParserUtil.getAttributeValue(startElement, SAMLProtocolQNames.ATTR_DESTINATION)); + response.setConsent(StaxParserUtil.getAttributeValue(startElement, SAMLProtocolQNames.ATTR_CONSENT)); + response.setInResponseTo(StaxParserUtil.getAttributeValue(startElement, SAMLProtocolQNames.ATTR_IN_RESPONSE_TO)); + } +} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/AbstractStaxXmlDSigParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/AbstractStaxXmlDSigParser.java new file mode 100644 index 0000000000..0d691d9a5b --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/AbstractStaxXmlDSigParser.java @@ -0,0 +1,40 @@ +/* + * 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.xmldsig; + +import org.keycloak.saml.common.parsers.AbstractStaxParser; +import org.keycloak.saml.processing.core.parsers.util.QNameEnumLookup; +import javax.xml.namespace.QName; + +/** + * + * @author hmlnarik + */ +public abstract class AbstractStaxXmlDSigParser extends AbstractStaxParser { + + protected static final QNameEnumLookup LOOKUP = new QNameEnumLookup(XmlDSigQNames.values()); + + public AbstractStaxXmlDSigParser(XmlDSigQNames expectedStartElement) { + super(expectedStartElement.getQName(), XmlDSigQNames.UNKNOWN_ELEMENT); + } + + @Override + protected XmlDSigQNames getElementFromName(QName name) { + return LOOKUP.from(name); + } + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/DsaKeyValueParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/DsaKeyValueParser.java new file mode 100644 index 0000000000..4c4c48c92e --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/DsaKeyValueParser.java @@ -0,0 +1,94 @@ +/* + * 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.xmldsig; + +import org.keycloak.dom.xmlsec.w3.xmldsig.DSAKeyValueType; +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; + +public class DsaKeyValueParser extends AbstractStaxXmlDSigParser { + + public static final DsaKeyValueParser INSTANCE = new DsaKeyValueParser(); + + private DsaKeyValueParser() { + super(XmlDSigQNames.DSA_KEY_VALUE); + } + + public static DsaKeyValueParser getInstance() { + return INSTANCE; + } + + @Override + protected DSAKeyValueType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new DSAKeyValueType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, DSAKeyValueType target, XmlDSigQNames element, StartElement elementDetail) throws ParsingException { + String text; + switch (element) { + case P: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setP(text.getBytes(GeneralConstants.SAML_CHARSET)); + break; + + case Q: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setQ(text.getBytes(GeneralConstants.SAML_CHARSET)); + break; + + case G: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setG(text.getBytes(GeneralConstants.SAML_CHARSET)); + break; + + case Y: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setY(text.getBytes(GeneralConstants.SAML_CHARSET)); + break; + + case J: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setJ(text.getBytes(GeneralConstants.SAML_CHARSET)); + break; + + case SEED: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setSeed(text.getBytes(GeneralConstants.SAML_CHARSET)); + break; + + case PGEN_COUNTER: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setPgenCounter(text.getBytes(GeneralConstants.SAML_CHARSET)); + 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/xmldsig/KeyInfoParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/KeyInfoParser.java new file mode 100644 index 0000000000..ee2dd347dc --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/KeyInfoParser.java @@ -0,0 +1,78 @@ +/* + * 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.xmldsig; + +import org.keycloak.dom.xmlsec.w3.xmldsig.KeyInfoType; +import org.keycloak.dom.xmlsec.w3.xmldsig.KeyValueType; +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; + +public class KeyInfoParser extends AbstractStaxXmlDSigParser { + + public static final KeyInfoParser INSTANCE = new KeyInfoParser(); + + private KeyInfoParser() { + super(XmlDSigQNames.KEY_INFO); + } + + public static KeyInfoParser getInstance() { + return INSTANCE; + } + + @Override + protected KeyInfoType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new KeyInfoType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, KeyInfoType target, XmlDSigQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case X509_DATA: + target.addContent(X509DataParser.getInstance().parse(xmlEventReader)); + break; + + case KEY_VALUE: + StaxParserUtil.advance(xmlEventReader); + StartElement startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); + KeyValueType keyValue; + + switch (LOOKUP.from(startElement.getName())) { + case RSA_KEY_VALUE: + keyValue = RsaKeyValueParser.getInstance().parse(xmlEventReader); + break; + + case DSA_KEY_VALUE: + keyValue = DsaKeyValueParser.getInstance().parse(xmlEventReader); + break; + + default: + String tag = StaxParserUtil.getElementName(startElement); + throw LOGGER.parserUnknownTag(tag, elementDetail.getLocation()); + } + + target.addContent(keyValue); + break; + + default: + // Ignore unknown tags + StaxParserUtil.bypassElementBlock(xmlEventReader); + } + } +} \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/RsaKeyValueParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/RsaKeyValueParser.java new file mode 100644 index 0000000000..fbb8b13c18 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/RsaKeyValueParser.java @@ -0,0 +1,64 @@ +/* + * 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.xmldsig; + +import org.keycloak.dom.xmlsec.w3.xmldsig.RSAKeyValueType; +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; + +public class RsaKeyValueParser extends AbstractStaxXmlDSigParser { + + public static final RsaKeyValueParser INSTANCE = new RsaKeyValueParser(); + + private RsaKeyValueParser() { + super(XmlDSigQNames.RSA_KEY_VALUE); + } + + public static RsaKeyValueParser getInstance() { + return INSTANCE; + } + + @Override + protected RSAKeyValueType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new RSAKeyValueType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, RSAKeyValueType target, XmlDSigQNames element, StartElement elementDetail) throws ParsingException { + String text; + switch (element) { + case MODULUS: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setModulus(text.getBytes(GeneralConstants.SAML_CHARSET)); + break; + + case EXPONENT: + StaxParserUtil.advance(xmlEventReader); + text = StaxParserUtil.getElementText(xmlEventReader); + target.setExponent(text.getBytes(GeneralConstants.SAML_CHARSET)); + 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/xmldsig/X509DataParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/X509DataParser.java new file mode 100644 index 0000000000..bbd339bbed --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/X509DataParser.java @@ -0,0 +1,66 @@ +/* + * 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.xmldsig; + +import org.keycloak.dom.xmlsec.w3.xmldsig.X509CertificateType; +import org.keycloak.dom.xmlsec.w3.xmldsig.X509DataType; +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; + +/** + * Base Class for all Response Type parsing for SAML2 + * + */ +public class X509DataParser extends AbstractStaxXmlDSigParser { + + private static final X509DataParser INSTANCE = new X509DataParser(); + + public X509DataParser() { + super(XmlDSigQNames.X509_DATA); + } + + public static X509DataParser getInstance() { + return INSTANCE; + } + + @Override + protected X509DataType instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new X509DataType(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, X509DataType target, XmlDSigQNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case X509_CERTIFICATE: + StaxParserUtil.advance(xmlEventReader); + String certValue = StaxParserUtil.getElementText(xmlEventReader); + + X509CertificateType cert = new X509CertificateType(); + cert.setEncodedCertificate(certValue.getBytes(GeneralConstants.SAML_CHARSET)); + target.add(cert); + + 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/xmldsig/XmlDSigQNames.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/XmlDSigQNames.java new file mode 100644 index 0000000000..db9197f4a0 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/xmldsig/XmlDSigQNames.java @@ -0,0 +1,96 @@ +/* + * 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.xmldsig; + +import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import javax.xml.namespace.QName; +import org.keycloak.saml.processing.core.parsers.util.HasQName; + +/** + * Elements from saml-schema-protocol-2.0.xsd + * @author hmlnarik + */ +public enum XmlDSigQNames implements HasQName { + + CANONICALIZATION_METHOD("CanonicalizationMethod"), + DIGEST_METHOD("DigestMethod"), + DIGEST_VALUE("DigestValue"), + DSA_KEY_VALUE("DSAKeyValue"), + EXPONENT("Exponent"), + G("G"), + HMAC_OUTPUT_LENGTH("HMACOutputLength"), + J("J"), + KEY_INFO("KeyInfo"), + KEY_NAME("KeyName"), + KEY_VALUE("KeyValue"), + MANIFEST("Manifest"), + MGMT_DATA("MgmtData"), + MODULUS("Modulus"), + OBJECT("Object"), + PGEN_COUNTER("PgenCounter"), + PGP_DATA("PGPData"), + PGP_KEY_ID("PGPKeyID"), + PGP_KEY_PACKET("PGPKeyPacket"), + P("P"), + Q("Q"), + REFERENCE("Reference"), + RETRIEVAL_METHOD("RetrievalMethod"), + RSA_KEY_VALUE("RSAKeyValue"), + SEED("Seed"), + SIGNATURE_METHOD("SignatureMethod"), + SIGNATURE_PROPERTIES("SignatureProperties"), + SIGNATURE_PROPERTY("SignatureProperty"), + SIGNATURE("Signature"), + SIGNATURE_VALUE("SignatureValue"), + SIGNED_INFO("SignedInfo"), + SPKI_DATA("SPKIData"), + SPKIS_EXP("SPKISexp"), + TRANSFORMS("Transforms"), + TRANSFORM("Transform"), + XPATH("XPath"), + X509_CERTIFICATE("X509Certificate"), + X509_CRL("X509CRL"), + X509_DATA("X509Data"), + X509_ISSUER_NAME("X509IssuerName"), + X509_ISSUER_SERIAL("X509IssuerSerial"), + X509_SERIAL_NUMBER("X509SerialNumber"), + X509_SKI("X509SKI"), + X509_SUBJECT_NAME("X509SubjectName"), + Y("Y"), + + UNKNOWN_ELEMENT("") + ; + + private final QName qName; + + private XmlDSigQNames(String localName) { + this(JBossSAMLURIConstants.XMLDSIG_NSURI, localName); + } + + private XmlDSigQNames(HasQName source) { + this.qName = source.getQName(); + } + + private XmlDSigQNames(JBossSAMLURIConstants nsUri, String localName) { + this.qName = new QName(nsUri == null ? null : nsUri.get(), localName); + } + + @Override + public QName getQName() { + return qName; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/HasQName.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/HasQName.java new file mode 100644 index 0000000000..e3254e8189 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/HasQName.java @@ -0,0 +1,29 @@ +/* + * 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.util; + +import javax.xml.namespace.QName; + +/** + * + * @author hmlnarik + */ +public interface HasQName { + + QName getQName(); + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/QNameEnumLookup.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/QNameEnumLookup.java new file mode 100644 index 0000000000..9a585f4030 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/QNameEnumLookup.java @@ -0,0 +1,64 @@ +/* + * 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.util; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; + +/** + * + * @author hmlnarik + */ +public class QNameEnumLookup & HasQName> { + + private final Map qNameConstants; + + public QNameEnumLookup(E[] e) { + Map q = new HashMap<>(e.length); + E old; + for (E c : e) { + QName qName = c.getQName(); + if ((old = q.put(qName, c)) != null) { + throw new IllegalStateException("Same name " + qName + " used for two distinct constants: " + c + ", " + old); + } + + // Add the relaxed version without namespace + if (qName.getNamespaceURI() != null && ! Objects.equals(qName.getNamespaceURI(), XMLConstants.NULL_NS_URI)) { + qName = new QName(qName.getLocalPart()); + if (q.containsKey(qName)) { + q.put(qName, null); + } else { + q.put(qName, c); + } + } + } + this.qNameConstants = Collections.unmodifiableMap(q); + } + + public E from(QName name) { + E c = qNameConstants.get(name); + if (c == null) { + name = new QName(name.getLocalPart()); + c = qNameConstants.get(name); + } + return c; + } +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAML11ParserUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAML11ParserUtil.java index 1cbfa7abb6..6f93ecdb36 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAML11ParserUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAML11ParserUtil.java @@ -49,7 +49,6 @@ import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.processing.core.parsers.saml.SAML11SubjectParser; import org.keycloak.saml.processing.core.saml.v1.SAML11Constants; -import org.keycloak.saml.processing.core.saml.v2.util.SignatureUtil; import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Element; @@ -61,6 +60,8 @@ import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import java.net.URI; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** * Utility for parsing SAML 1.1 payload @@ -106,11 +107,11 @@ public class SAML11ParserUtil { if (xmlEvent instanceof EndElement) { xmlEvent = StaxParserUtil.getNextEvent(xmlEventReader); EndElement endElement = (EndElement) xmlEvent; - String endElementTag = StaxParserUtil.getEndElementName(endElement); + String endElementTag = StaxParserUtil.getElementName(endElement); if (endElementTag.equals(SAML11Constants.AUTHENTICATION_STATEMENT)) break; else - throw logger.parserUnknownEndElement(endElementTag); + throw logger.parserUnknownEndElement(endElementTag, xmlEvent.getLocation()); } startElement = null; @@ -122,7 +123,7 @@ public class SAML11ParserUtil { if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (JBossSAMLConstants.SUBJECT.get().equalsIgnoreCase(tag)) { SAML11SubjectParser subjectParser = new SAML11SubjectParser(); @@ -199,7 +200,7 @@ public class SAML11ParserUtil { if (xmlEvent instanceof StartElement) { startElement = (StartElement) xmlEvent; - String startTag = StaxParserUtil.getStartElementName(startElement); + String startTag = StaxParserUtil.getElementName(startElement); if (startTag.equals(SAML11Constants.CONFIRMATION_METHOD)) { startElement = StaxParserUtil.getNextStartElement(xmlEventReader); @@ -264,7 +265,7 @@ public class SAML11ParserUtil { XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); if (!(xmlEvent instanceof EndElement)) { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (tag.equals(WSTrustConstants.XMLDSig.KEYINFO)) { KeyInfoType keyInfo = parseKeyInfo(xmlEventReader); subjectConfirmationData.setAnyType(keyInfo); @@ -371,7 +372,7 @@ public class SAML11ParserUtil { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (JBossSAMLConstants.ATTRIBUTE.get().equals(tag)) break; @@ -440,7 +441,7 @@ public class SAML11ParserUtil { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (SAML11Constants.ACTION.equals(tag)) { startElement = StaxParserUtil.getNextStartElement(xmlEventReader); @@ -512,14 +513,14 @@ public class SAML11ParserUtil { startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); if (startElement == null) break; - String tag = StaxParserUtil.getStartElementName(startElement); + String tag = StaxParserUtil.getElementName(startElement); if (SAML11Constants.AUDIENCE_RESTRICTION_CONDITION.equals(tag)) { startElement = StaxParserUtil.getNextStartElement(xmlEventReader); SAML11AudienceRestrictionCondition restrictCond = new SAML11AudienceRestrictionCondition(); startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - if (StaxParserUtil.getStartElementName(startElement).equals(JBossSAMLConstants.AUDIENCE.get())) { + if (StaxParserUtil.getElementName(startElement).equals(JBossSAMLConstants.AUDIENCE.get())) { restrictCond.add(URI.create(StaxParserUtil.getElementText(xmlEventReader))); } EndElement theEndElement = StaxParserUtil.getNextEndElement(xmlEventReader); @@ -542,15 +543,15 @@ public class SAML11ParserUtil { while (xmlEventReader.hasNext()) { xmlEvent = StaxParserUtil.peek(xmlEventReader); if (xmlEvent instanceof EndElement) { - tag = StaxParserUtil.getEndElementName((EndElement) xmlEvent); + tag = StaxParserUtil.getElementName((EndElement) xmlEvent); if (tag.equals(WSTrustConstants.XMLDSig.KEYINFO)) { xmlEvent = StaxParserUtil.getNextEndElement(xmlEventReader); break; } else - throw logger.parserUnknownEndElement(tag); + throw logger.parserUnknownEndElement(tag, xmlEvent.getLocation()); } startElement = (StartElement) xmlEvent; - tag = StaxParserUtil.getStartElementName(startElement); + tag = StaxParserUtil.getElementName(startElement); if (tag.equals(WSTrustConstants.XMLEnc.ENCRYPTED_KEY)) { keyInfo.addContent(StaxParserUtil.getDOMElement(xmlEventReader)); } else if (tag.equals(WSTrustConstants.XMLDSig.X509DATA)) { @@ -574,7 +575,7 @@ public class SAML11ParserUtil { KeyValueType keyValue = null; startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - tag = StaxParserUtil.getStartElementName(startElement); + tag = StaxParserUtil.getElementName(startElement); if (tag.equals(WSTrustConstants.XMLDSig.RSA_KEYVALUE)) { keyValue = parseRSAKeyValue(xmlEventReader); } else if (tag.equals(WSTrustConstants.XMLDSig.DSA_KEYVALUE)) { @@ -603,16 +604,16 @@ public class SAML11ParserUtil { while (xmlEventReader.hasNext()) { xmlEvent = StaxParserUtil.peek(xmlEventReader); if (xmlEvent instanceof EndElement) { - tag = StaxParserUtil.getEndElementName((EndElement) xmlEvent); + tag = StaxParserUtil.getElementName((EndElement) xmlEvent); if (tag.equals(WSTrustConstants.XMLDSig.RSA_KEYVALUE)) { xmlEvent = StaxParserUtil.getNextEndElement(xmlEventReader); break; } else - throw logger.parserUnknownEndElement(tag); + throw logger.parserUnknownEndElement(tag, xmlEvent.getLocation()); } startElement = (StartElement) xmlEvent; - tag = StaxParserUtil.getStartElementName(startElement); + tag = StaxParserUtil.getElementName(startElement); if (tag.equals(WSTrustConstants.XMLDSig.MODULUS)) { startElement = StaxParserUtil.getNextStartElement(xmlEventReader); String text = StaxParserUtil.getElementText(xmlEventReader); @@ -632,7 +633,48 @@ public class SAML11ParserUtil { StaxParserUtil.validate(startElement, WSTrustConstants.XMLDSig.DSA_KEYVALUE); Element dsaElement = StaxParserUtil.getDOMElement(xmlEventReader); - return SignatureUtil.getDSAKeyValue(dsaElement); + return getDSAKeyValue(dsaElement); + } + + /** + * Given a dsig:DSAKeyValue element, return {@link DSAKeyValueType} + * + * @param element + * + * @return + * + * @throws org.keycloak.saml.common.exceptions.ParsingException + */ + private static DSAKeyValueType getDSAKeyValue(Element element) throws ParsingException { + DSAKeyValueType dsa = new DSAKeyValueType(); + NodeList nl = element.getChildNodes(); + int length = nl.getLength(); + + for (int i = 0; i < length; i++) { + Node node = nl.item(i); + if (node instanceof Element) { + Element childElement = (Element) node; + String tag = childElement.getLocalName(); + + byte[] text = childElement.getTextContent().getBytes(GeneralConstants.SAML_CHARSET); + + if (WSTrustConstants.XMLDSig.P.equals(tag)) { + dsa.setP(text); + } else if (WSTrustConstants.XMLDSig.Q.equals(tag)) { + dsa.setQ(text); + } else if (WSTrustConstants.XMLDSig.G.equals(tag)) { + dsa.setG(text); + } else if (WSTrustConstants.XMLDSig.Y.equals(tag)) { + dsa.setY(text); + } else if (WSTrustConstants.XMLDSig.SEED.equals(tag)) { + dsa.setSeed(text); + } else if (WSTrustConstants.XMLDSig.PGEN_COUNTER.equals(tag)) { + dsa.setPgenCounter(text); + } + } + } + + return dsa; } /** @@ -655,13 +697,13 @@ public class SAML11ParserUtil { if (StaxParserUtil.matches(endElement, SAML11Constants.ATTRIBUTE_QUERY)) break; else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); + throw logger.parserUnknownEndElement(StaxParserUtil.getElementName(endElement), xmlEvent.getLocation()); } if (xmlEvent instanceof StartElement) { startElement = (StartElement) xmlEvent; - String startTag = StaxParserUtil.getStartElementName(startElement); + String startTag = StaxParserUtil.getElementName(startElement); if (startTag.equals(JBossSAMLConstants.SUBJECT.get())) { SAML11SubjectParser parser = new SAML11SubjectParser(); @@ -694,13 +736,13 @@ public class SAML11ParserUtil { if (StaxParserUtil.matches(endElement, SAML11Constants.AUTHENTICATION_QUERY)) break; else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); + throw logger.parserUnknownEndElement(StaxParserUtil.getElementName(endElement), xmlEvent.getLocation()); } if (xmlEvent instanceof StartElement) { startElement = (StartElement) xmlEvent; - String startTag = StaxParserUtil.getStartElementName(startElement); + String startTag = StaxParserUtil.getElementName(startElement); if (startTag.equals(JBossSAMLConstants.SUBJECT.get())) { SAML11SubjectParser parser = new SAML11SubjectParser(); @@ -733,13 +775,13 @@ public class SAML11ParserUtil { if (StaxParserUtil.matches(endElement, SAML11Constants.AUTHORIZATION_DECISION_QUERY)) break; else - throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); + throw logger.parserUnknownEndElement(StaxParserUtil.getElementName(endElement), xmlEvent.getLocation()); } if (xmlEvent instanceof StartElement) { startElement = (StartElement) xmlEvent; - String startTag = StaxParserUtil.getStartElementName(startElement); + String startTag = StaxParserUtil.getElementName(startElement); if (startTag.equals(JBossSAMLConstants.SUBJECT.get())) { SAML11SubjectParser parser = new SAML11SubjectParser(); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java index 8b39ff0062..c3b44bcfdd 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/util/SAMLParserUtil.java @@ -15,51 +15,16 @@ * limitations under the License. */ package org.keycloak.saml.processing.core.parsers.util; - -import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; -import org.keycloak.dom.saml.v2.assertion.AttributeStatementType.ASTChoiceType; -import org.keycloak.dom.saml.v2.assertion.AttributeType; -import org.keycloak.dom.saml.v2.assertion.AuthnContextClassRefType; -import org.keycloak.dom.saml.v2.assertion.AuthnContextDeclRefType; -import org.keycloak.dom.saml.v2.assertion.AuthnContextDeclType; -import org.keycloak.dom.saml.v2.assertion.AuthnContextType; -import org.keycloak.dom.saml.v2.assertion.AuthnStatementType; import org.keycloak.dom.saml.v2.assertion.NameIDType; -import org.keycloak.dom.saml.v2.assertion.SubjectLocalityType; -import org.keycloak.dom.xmlsec.w3.xmldsig.DSAKeyValueType; -import org.keycloak.dom.xmlsec.w3.xmldsig.KeyInfoType; -import org.keycloak.dom.xmlsec.w3.xmldsig.KeyValueType; -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.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.constants.WSTrustConstants; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.common.util.StringUtil; -import org.keycloak.saml.processing.core.saml.v2.util.SignatureUtil; -import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; -import org.w3c.dom.Element; - -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.namespace.QName; +import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionQNames; +import java.util.Objects; import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLEventWriter; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.events.Attribute; -import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import java.io.StringWriter; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; /** * Utility methods for SAML Parser @@ -69,472 +34,7 @@ import java.util.StringTokenizer; */ public class SAMLParserUtil { - private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); - - public static KeyInfoType parseKeyInfo(XMLEventReader xmlEventReader) throws ParsingException { - KeyInfoType keyInfo = new KeyInfoType(); - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, WSTrustConstants.XMLDSig.KEYINFO); - - XMLEvent xmlEvent = null; - String tag = null; - - while (xmlEventReader.hasNext()) { - xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - tag = StaxParserUtil.getEndElementName((EndElement) xmlEvent); - if (tag.equals(WSTrustConstants.XMLDSig.KEYINFO)) { - xmlEvent = StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } else - throw logger.parserUnknownEndElement(tag); - } - startElement = (StartElement) xmlEvent; - tag = StaxParserUtil.getStartElementName(startElement); - if (tag.equals(WSTrustConstants.XMLEnc.ENCRYPTED_KEY)) { - keyInfo.addContent(StaxParserUtil.getDOMElement(xmlEventReader)); - } else if (tag.equals(WSTrustConstants.XMLDSig.X509DATA)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - X509DataType x509 = new X509DataType(); - - // Let us go for the X509 certificate - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, WSTrustConstants.XMLDSig.X509CERT); - - X509CertificateType cert = new X509CertificateType(); - String certValue = StaxParserUtil.getElementText(xmlEventReader); - cert.setEncodedCertificate(certValue.getBytes(GeneralConstants.SAML_CHARSET)); - x509.add(cert); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, WSTrustConstants.XMLDSig.X509DATA); - keyInfo.addContent(x509); - } else if (tag.equals(WSTrustConstants.XMLDSig.KEYVALUE)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - KeyValueType keyValue = null; - - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - tag = StaxParserUtil.getStartElementName(startElement); - if (tag.equals(WSTrustConstants.XMLDSig.RSA_KEYVALUE)) { - keyValue = parseRSAKeyValue(xmlEventReader); - } else if (tag.equals(WSTrustConstants.XMLDSig.DSA_KEYVALUE)) { - keyValue = parseDSAKeyValue(xmlEventReader); - } else - throw logger.parserUnknownTag(tag, startElement.getLocation()); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, WSTrustConstants.XMLDSig.KEYVALUE); - - keyInfo.addContent(keyValue); - } - } - return keyInfo; - } - - private static RSAKeyValueType parseRSAKeyValue(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, WSTrustConstants.XMLDSig.RSA_KEYVALUE); - - XMLEvent xmlEvent = null; - String tag = null; - - RSAKeyValueType rsaKeyValue = new RSAKeyValueType(); - - while (xmlEventReader.hasNext()) { - xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - tag = StaxParserUtil.getEndElementName((EndElement) xmlEvent); - if (tag.equals(WSTrustConstants.XMLDSig.RSA_KEYVALUE)) { - xmlEvent = StaxParserUtil.getNextEndElement(xmlEventReader); - break; - } else - throw logger.parserUnknownEndElement(tag); - } - - startElement = (StartElement) xmlEvent; - tag = StaxParserUtil.getStartElementName(startElement); - if (tag.equals(WSTrustConstants.XMLDSig.MODULUS)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String text = StaxParserUtil.getElementText(xmlEventReader); - rsaKeyValue.setModulus(text.getBytes(GeneralConstants.SAML_CHARSET)); - } else if (tag.equals(WSTrustConstants.XMLDSig.EXPONENT)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String text = StaxParserUtil.getElementText(xmlEventReader); - rsaKeyValue.setExponent(text.getBytes(GeneralConstants.SAML_CHARSET)); - } else - throw logger.parserUnknownTag(tag, startElement.getLocation()); - } - return rsaKeyValue; - } - - private static DSAKeyValueType parseDSAKeyValue(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, WSTrustConstants.XMLDSig.DSA_KEYVALUE); - - Element dsaElement = StaxParserUtil.getDOMElement(xmlEventReader); - return SignatureUtil.getDSAKeyValue(dsaElement); - } - - /** - * Parse an {@code AttributeStatementType} - * - * @param xmlEventReader - * - * @return - * - * @throws ParsingException - */ - public static AttributeStatementType parseAttributeStatement(XMLEventReader xmlEventReader) throws ParsingException { - AttributeStatementType attributeStatementType = new AttributeStatementType(); - - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String ATTRIBSTATEMT = JBossSAMLConstants.ATTRIBUTE_STATEMENT.get(); - StaxParserUtil.validate(startElement, ATTRIBSTATEMT); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.ATTRIBUTE_STATEMENT.get()); - break; - } - // Get the next start element - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - String tag = startElement.getName().getLocalPart(); - if (JBossSAMLConstants.ATTRIBUTE.get().equals(tag)) { - AttributeType attribute = parseAttribute(xmlEventReader); - attributeStatementType.addAttribute(new ASTChoiceType(attribute)); - } else - throw logger.parserUnknownTag(tag, startElement.getLocation()); - } - return attributeStatementType; - } - - /** - * Parse an {@code AttributeType} - * - * @param xmlEventReader - * - * @return - * - * @throws ParsingException - */ - public static AttributeType parseAttribute(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ATTRIBUTE.get()); - AttributeType attributeType = null; - - Attribute name = startElement.getAttributeByName(new QName(JBossSAMLConstants.NAME.get())); - if (name == null) - throw logger.parserRequiredAttribute("Name"); - attributeType = new AttributeType(StaxParserUtil.getAttributeValue(name)); - - parseAttributeType(xmlEventReader, startElement, JBossSAMLConstants.ATTRIBUTE.get(), attributeType); - - return attributeType; - } - - /** - * Parse an {@code AttributeType} - * - * @param xmlEventReader - * - * @throws ParsingException - */ - public static void parseAttributeType(XMLEventReader xmlEventReader, StartElement startElement, String rootTag, - AttributeType attributeType) throws ParsingException { - // Look for X500 Encoding - QName x500EncodingName = new QName(JBossSAMLURIConstants.X500_NSURI.get(), JBossSAMLConstants.ENCODING.get(), - JBossSAMLURIConstants.X500_PREFIX.get()); - Attribute x500EncodingAttr = startElement.getAttributeByName(x500EncodingName); - - if (x500EncodingAttr != null) { - attributeType.getOtherAttributes().put(x500EncodingAttr.getName(), - StaxParserUtil.getAttributeValue(x500EncodingAttr)); - } - - Attribute friendlyName = startElement.getAttributeByName(new QName(JBossSAMLConstants.FRIENDLY_NAME.get())); - if (friendlyName != null) - attributeType.setFriendlyName(StaxParserUtil.getAttributeValue(friendlyName)); - - Attribute nameFormat = startElement.getAttributeByName(new QName(JBossSAMLConstants.NAME_FORMAT.get())); - if (nameFormat != null) - attributeType.setNameFormat(StaxParserUtil.getAttributeValue(nameFormat)); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof EndElement) { - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - if (StaxParserUtil.matches(end, rootTag)) - break; - } - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.ATTRIBUTE.get().equals(tag)) - break; - - if (JBossSAMLConstants.ATTRIBUTE_VALUE.get().equals(tag)) { - Object attributeValue = parseAttributeValue(xmlEventReader); - attributeType.addAttributeValue(attributeValue); - } else - throw logger.parserUnknownTag(tag, startElement.getLocation()); - } - } - - /** - * Parse Attribute value - * - * @param xmlEventReader - * - * @return - * - * @throws ParsingException - */ - public static Object parseAttributeValue(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.ATTRIBUTE_VALUE.get()); - - Attribute type = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XSI_NSURI.get(), JBossSAMLConstants.TYPE.get(), JBossSAMLURIConstants.XSI_PREFIX.get())); - Attribute nil = startElement.getAttributeByName(new QName(JBossSAMLURIConstants.XSI_NSURI.get(), "nil", JBossSAMLURIConstants.XSI_PREFIX.get())); - if (nil != null) { - String nilValue = StaxParserUtil.getAttributeValue(nil); - if (nilValue != null - && (nilValue.equalsIgnoreCase("true") || nilValue.equals("1"))) { - String elementText = StaxParserUtil.getElementText(xmlEventReader); - if (elementText == null || elementText.isEmpty()) { - return null; - } else { - throw logger.nullValueError("nil attribute is not in SAML20 format"); - } - } else { - throw logger.parserRequiredAttribute(JBossSAMLURIConstants.XSI_PREFIX.get() + ":nil"); - } - } - if (type == null) { - if (StaxParserUtil.hasTextAhead(xmlEventReader)) { - return StaxParserUtil.getElementText(xmlEventReader); - } - // Else we may have Child Element - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent instanceof StartElement) { - startElement = (StartElement) xmlEvent; - String tag = StaxParserUtil.getStartElementName(startElement); - if (tag.equals(JBossSAMLConstants.NAMEID.get())) { - return parseNameIDType(xmlEventReader); - } - } else if (xmlEvent instanceof EndElement) { - // consume the end element tag - EndElement end = StaxParserUtil.getNextEndElement(xmlEventReader); - String endElementTag = StaxParserUtil.getEndElementName(end); - if (! StaxParserUtil.matches(end, JBossSAMLConstants.ATTRIBUTE_VALUE.get())) - throw logger.parserUnknownEndElement(endElementTag); - return ""; - } - - // when no type attribute assigned -> assume anyType - return parseAnyTypeAsString(xmlEventReader); - } - // RK Added an additional type check for base64Binary type as calheers is passing this type - String typeValue = StaxParserUtil.getAttributeValue(type); - if (typeValue.contains(":string")) { - return StaxParserUtil.getElementText(xmlEventReader); - } else if (typeValue.contains(":anyType")) { - return parseAnyTypeAsString(xmlEventReader); - } else if(typeValue.contains(":base64Binary")){ - return StaxParserUtil.getElementText(xmlEventReader); - } else if(typeValue.contains(":boolean")){ - return StaxParserUtil.getElementText(xmlEventReader); - } - - throw logger.parserUnknownXSI(typeValue); - } - - public static String parseAnyTypeAsString(XMLEventReader xmlEventReader) throws ParsingException { - try { - XMLEvent event = xmlEventReader.peek(); - if (event.isStartElement()) { - event = xmlEventReader.nextTag(); - StringWriter sw = new StringWriter(); - XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(sw); - //QName tagName = event.asStartElement().getName(); - int tagLevel = 1; - do { - writer.add(event); - event = (XMLEvent) xmlEventReader.next(); - if (event.isStartElement()) { - tagLevel++; - } - if (event.isEndElement()) { - tagLevel--; - } - } while (xmlEventReader.hasNext() && tagLevel > 0); - writer.add(event); - writer.flush(); - return sw.toString(); - } else { - return StaxParserUtil.getElementText(xmlEventReader); - } - } catch (Exception e) { - throw logger.parserError(e); - } - } - - /** - * Parse the AuthnStatement inside the assertion - * - * @param xmlEventReader - * - * @return - * - * @throws ParsingException - */ - public static AuthnStatementType parseAuthnStatement(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String AUTHNSTATEMENT = JBossSAMLConstants.AUTHN_STATEMENT.get(); - StaxParserUtil.validate(startElement, AUTHNSTATEMENT); - - Attribute authnInstant = startElement.getAttributeByName(new QName("AuthnInstant")); - if (authnInstant == null) - throw logger.parserRequiredAttribute("AuthnInstant"); - - XMLGregorianCalendar issueInstant = XMLTimeUtil.parse(StaxParserUtil.getAttributeValue(authnInstant)); - AuthnStatementType authnStatementType = new AuthnStatementType(issueInstant); - - Attribute sessionIndex = startElement.getAttributeByName(new QName("SessionIndex")); - if (sessionIndex != null) - authnStatementType.setSessionIndex(StaxParserUtil.getAttributeValue(sessionIndex)); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - - if (xmlEvent instanceof EndElement) { - xmlEvent = StaxParserUtil.getNextEvent(xmlEventReader); - EndElement endElement = (EndElement) xmlEvent; - String endElementTag = StaxParserUtil.getEndElementName(endElement); - if (endElementTag.equals(AUTHNSTATEMENT)) - break; - else - throw logger.parserUnknownEndElement(endElementTag); - } - startElement = null; - - if (xmlEvent instanceof StartElement) { - startElement = (StartElement) xmlEvent; - } else { - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - } - if (startElement == null) - break; - - String tag = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.SUBJECT_LOCALITY.get().equals(tag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - SubjectLocalityType subjectLocalityType = new SubjectLocalityType(); - Attribute address = startElement.getAttributeByName(new QName(JBossSAMLConstants.ADDRESS.get())); - if (address != null) { - subjectLocalityType.setAddress(StaxParserUtil.getAttributeValue(address)); - } - Attribute dns = startElement.getAttributeByName(new QName(JBossSAMLConstants.DNS_NAME.get())); - if (dns != null) { - subjectLocalityType.setDNSName(StaxParserUtil.getAttributeValue(dns)); - } - authnStatementType.setSubjectLocality(subjectLocalityType); - StaxParserUtil.validate(StaxParserUtil.getNextEndElement(xmlEventReader), - JBossSAMLConstants.SUBJECT_LOCALITY.get()); - } else if (JBossSAMLConstants.AUTHN_CONTEXT.get().equals(tag)) { - authnStatementType.setAuthnContext(parseAuthnContextType(xmlEventReader)); - } else - throw logger.parserUnknownTag(tag, startElement.getLocation()); - - } - - return authnStatementType; - } - - /** - * Parse the AuthnContext Type inside the AuthnStatement - * - * @param xmlEventReader - * - * @return - * - * @throws ParsingException - */ - public static AuthnContextType parseAuthnContextType(XMLEventReader xmlEventReader) throws ParsingException { - AuthnContextType authnContextType = new AuthnContextType(); - - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, JBossSAMLConstants.AUTHN_CONTEXT.get()); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - - if (xmlEvent instanceof EndElement) { - xmlEvent = StaxParserUtil.getNextEvent(xmlEventReader); - EndElement endElement = (EndElement) xmlEvent; - String endElementTag = StaxParserUtil.getEndElementName(endElement); - if (endElementTag.equals(JBossSAMLConstants.AUTHN_CONTEXT.get())) - break; - else - throw logger.parserUnknownEndElement(endElementTag); - } - startElement = null; - - if (xmlEvent instanceof StartElement) { - startElement = (StartElement) xmlEvent; - } else { - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - } - if (startElement == null) - break; - - String tag = StaxParserUtil.getStartElementName(startElement); - - if (JBossSAMLConstants.AUTHN_CONTEXT_DECLARATION.get().equals(tag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - - Element dom = StaxParserUtil.getDOMElement(xmlEventReader); - - AuthnContextDeclType authnContextDecl = new AuthnContextDeclType(dom); - AuthnContextType.AuthnContextTypeSequence authnContextSequence = authnContextType.new AuthnContextTypeSequence(); - authnContextSequence.setAuthnContextDecl(authnContextDecl); - authnContextType.setSequence(authnContextSequence); - - EndElement endElement = StaxParserUtil.getNextEndElement(xmlEventReader); - StaxParserUtil.validate(endElement, JBossSAMLConstants.AUTHN_CONTEXT_DECLARATION.get()); - } else if (JBossSAMLConstants.AUTHN_CONTEXT_DECLARATION_REF.get().equals(tag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String text = StaxParserUtil.getElementText(xmlEventReader); - - AuthnContextDeclRefType aAuthnContextDeclType = new AuthnContextDeclRefType(URI.create(text)); - authnContextType.addURIType(aAuthnContextDeclType); - } else if (JBossSAMLConstants.AUTHN_CONTEXT_CLASS_REF.get().equals(tag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String text = StaxParserUtil.getElementText(xmlEventReader); - - AuthnContextClassRefType aAuthnContextClassRefType = new AuthnContextClassRefType(URI.create(text)); - AuthnContextType.AuthnContextTypeSequence authnContextSequence = authnContextType.new AuthnContextTypeSequence(); - authnContextSequence.setClassRef(aAuthnContextClassRefType); - - authnContextType.setSequence(authnContextSequence); - } else if (JBossSAMLConstants.AUTHENTICATING_AUTHORITY.get().equals(tag)) { - startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - String text = StaxParserUtil.getElementText(xmlEventReader); - authnContextType.addAuthenticatingAuthority(URI.create(text)); - } else - throw logger.parserUnknownTag(tag, startElement.getLocation()); - } - - return authnContextType; - } + private static final PicketLinkLogger LOGGER = PicketLinkLoggerFactory.getLogger(); /** * Parse a {@code NameIDType} @@ -547,27 +47,12 @@ public class SAMLParserUtil { */ public static NameIDType parseNameIDType(XMLEventReader xmlEventReader) throws ParsingException { StartElement nameIDElement = StaxParserUtil.getNextStartElement(xmlEventReader); + NameIDType nameID = new NameIDType(); - - Attribute nameQualifier = nameIDElement.getAttributeByName(new QName(JBossSAMLConstants.NAME_QUALIFIER.get())); - if (nameQualifier != null) { - nameID.setNameQualifier(StaxParserUtil.getAttributeValue(nameQualifier)); - } - - Attribute format = nameIDElement.getAttributeByName(new QName(JBossSAMLConstants.FORMAT.get())); - if (format != null) { - nameID.setFormat(URI.create(StaxParserUtil.getAttributeValue(format))); - } - - Attribute spProvidedID = nameIDElement.getAttributeByName(new QName(JBossSAMLConstants.SP_PROVIDED_ID.get())); - if (spProvidedID != null) { - nameID.setSPProvidedID(StaxParserUtil.getAttributeValue(spProvidedID)); - } - - Attribute spNameQualifier = nameIDElement.getAttributeByName(new QName(JBossSAMLConstants.SP_NAME_QUALIFIER.get())); - if (spNameQualifier != null) { - nameID.setSPNameQualifier(StaxParserUtil.getAttributeValue(spNameQualifier)); - } + nameID.setFormat(StaxParserUtil.getUriAttributeValue(nameIDElement, SAMLAssertionQNames.ATTR_FORMAT)); + nameID.setNameQualifier(StaxParserUtil.getAttributeValue(nameIDElement, SAMLAssertionQNames.ATTR_NAME_QUALIFIER)); + nameID.setSPProvidedID(StaxParserUtil.getAttributeValue(nameIDElement, SAMLAssertionQNames.ATTR_SP_PROVIDED_ID)); + nameID.setSPNameQualifier(StaxParserUtil.getAttributeValue(nameIDElement, SAMLAssertionQNames.ATTR_SP_NAME_QUALIFIER)); String nameIDValue = StaxParserUtil.getElementText(xmlEventReader); nameID.setValue(nameIDValue); @@ -575,24 +60,11 @@ public class SAMLParserUtil { return nameID; } - /** - * Parse a space delimited list of strings - * - * @param startElement - * - * @return - */ - public static List parseProtocolEnumeration(StartElement startElement) { - List protocolEnum = new ArrayList(); - Attribute proto = startElement.getAttributeByName(new QName(JBossSAMLConstants.PROTOCOL_SUPPORT_ENUMERATION.get())); - String val = StaxParserUtil.getAttributeValue(proto); - if (StringUtil.isNotNull(val)) { - StringTokenizer st = new StringTokenizer(val); - while (st.hasMoreTokens()) { - protocolEnum.add(st.nextToken()); - } - + public static void validateAttributeValue(StartElement element, HasQName attributeName, String expectedValue) throws ParsingException { + String value = StaxParserUtil.getRequiredAttributeValue(element, attributeName); + if (! Objects.equals(expectedValue, value)) { + throw LOGGER.parserException(new RuntimeException( + String.format("%s %s required to be \"%s\"", element.getName(), attributeName.getQName(), expectedValue))); } - return protocolEnum; } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/factories/JBossSAMLAuthnResponseFactory.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/factories/JBossSAMLAuthnResponseFactory.java index 15e8165523..1adc7049de 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/factories/JBossSAMLAuthnResponseFactory.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/factories/JBossSAMLAuthnResponseFactory.java @@ -80,7 +80,7 @@ public class JBossSAMLAuthnResponseFactory { public static StatusType createStatusTypeForResponder(String statusCodeURI) { StatusCodeType topLevelCode = new StatusCodeType(); - topLevelCode.setValue(URI.create(JBossSAMLURIConstants.STATUS_RESPONDER.get())); + topLevelCode.setValue(JBossSAMLURIConstants.STATUS_RESPONDER.getUri()); StatusCodeType secondLevelCode = new StatusCodeType(); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java index 244fb7d182..0de9c86edd 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java @@ -604,7 +604,7 @@ public class AssertionUtil { newDoc.appendChild(importedNode); Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey); - SAMLParser parser = new SAMLParser(); + SAMLParser parser = SAMLParser.getInstance(); JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement); AssertionType assertion = (AssertionType) parser.parse(parser.createEventReader(DocumentUtil diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/SignatureUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/SignatureUtil.java index 1ba4d74088..52c6b90756 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/SignatureUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/SignatureUtil.java @@ -172,81 +172,6 @@ public class SignatureUtil { return sig.verify(signatureValue); } - - /** - * Given a dsig:DSAKeyValue element, return {@link DSAKeyValueType} - * - * @param element - * - * @return - * - * @throws org.keycloak.saml.common.exceptions.ParsingException - */ - public static DSAKeyValueType getDSAKeyValue(Element element) throws ParsingException { - DSAKeyValueType dsa = new DSAKeyValueType(); - NodeList nl = element.getChildNodes(); - int length = nl.getLength(); - - for (int i = 0; i < length; i++) { - Node node = nl.item(i); - if (node instanceof Element) { - Element childElement = (Element) node; - String tag = childElement.getLocalName(); - - byte[] text = childElement.getTextContent().getBytes(GeneralConstants.SAML_CHARSET); - - if (WSTrustConstants.XMLDSig.P.equals(tag)) { - dsa.setP(text); - } else if (WSTrustConstants.XMLDSig.Q.equals(tag)) { - dsa.setQ(text); - } else if (WSTrustConstants.XMLDSig.G.equals(tag)) { - dsa.setG(text); - } else if (WSTrustConstants.XMLDSig.Y.equals(tag)) { - dsa.setY(text); - } else if (WSTrustConstants.XMLDSig.SEED.equals(tag)) { - dsa.setSeed(text); - } else if (WSTrustConstants.XMLDSig.PGEN_COUNTER.equals(tag)) { - dsa.setPgenCounter(text); - } - } - } - - return dsa; - } - - /** - * Given a dsig:DSAKeyValue element, return {@link DSAKeyValueType} - * - * @param element - * - * @return - * - * @throws ParsingException - */ - public static RSAKeyValueType getRSAKeyValue(Element element) throws ParsingException { - RSAKeyValueType rsa = new RSAKeyValueType(); - NodeList nl = element.getChildNodes(); - int length = nl.getLength(); - - for (int i = 0; i < length; i++) { - Node node = nl.item(i); - if (node instanceof Element) { - Element childElement = (Element) node; - String tag = childElement.getLocalName(); - - byte[] text = childElement.getTextContent().getBytes(GeneralConstants.SAML_CHARSET); - - if (WSTrustConstants.XMLDSig.MODULUS.equals(tag)) { - rsa.setModulus(text); - } else if (WSTrustConstants.XMLDSig.EXPONENT.equals(tag)) { - rsa.setExponent(text); - } - } - } - - return rsa; - } - /** *

* Creates a {@code KeyValueType} that wraps the specified public key. This method supports DSA and RSA keys. diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java index 068c91a051..f9986f153d 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java @@ -253,7 +253,7 @@ public class BaseWriter { return; } - StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.EXTENSIONS.get(), PROTOCOL_NSURI.get()); + StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.EXTENSIONS__PROTOCOL.get(), PROTOCOL_NSURI.get()); for (Object o : extensions.getAny()) { if (o instanceof Node) { diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLAssertionWriter.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLAssertionWriter.java index 41461bf889..5ad84daa45 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLAssertionWriter.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLAssertionWriter.java @@ -253,14 +253,14 @@ public class SAMLAssertionWriter extends BaseWriter { if (uriTypes != null) { for (URIType uriType : uriTypes) { if (uriType instanceof AuthnContextDeclType) { - StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.AUTHN_CONTEXT_DECLARATION.get(), + StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.AUTHN_CONTEXT_DECL.get(), ASSERTION_NSURI.get()); StaxUtil.writeCharacters(writer, uriType.getValue().toASCIIString()); StaxUtil.writeEndElement(writer); } if (uriType instanceof AuthnContextDeclRefType) { StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, - JBossSAMLConstants.AUTHN_CONTEXT_DECLARATION_REF.get(), ASSERTION_NSURI.get()); + JBossSAMLConstants.AUTHN_CONTEXT_DECL_REF.get(), ASSERTION_NSURI.get()); StaxUtil.writeCharacters(writer, uriType.getValue().toASCIIString()); StaxUtil.writeEndElement(writer); } @@ -286,12 +286,12 @@ public class SAMLAssertionWriter extends BaseWriter { StaxUtil.writeCharacters(writer, uriType.getValue().toString()); StaxUtil.writeEndElement(writer); } else if (uriType instanceof AuthnContextDeclRefType) { - StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.AUTHN_CONTEXT_DECLARATION_REF.get(), + StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.AUTHN_CONTEXT_DECL_REF.get(), ASSERTION_NSURI.get()); StaxUtil.writeCharacters(writer, uriType.getValue().toString()); StaxUtil.writeEndElement(writer); } else if (uriType instanceof AuthnContextDeclType) { - StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.AUTHN_CONTEXT_DECLARATION.get(), + StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.AUTHN_CONTEXT_DECL.get(), ASSERTION_NSURI.get()); StaxUtil.writeCharacters(writer, uriType.getValue().toString()); StaxUtil.writeEndElement(writer); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java index a3b2608980..a9e5fba2c1 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java @@ -70,7 +70,7 @@ public class SAMLResponseWriter extends BaseWriter { public void write(ResponseType response, boolean forceWriteDsigNamespace) throws ProcessingException { Element sig = response.getSignature(); - StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.RESPONSE.get(), JBossSAMLURIConstants.PROTOCOL_NSURI.get()); + StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.RESPONSE__PROTOCOL.get(), JBossSAMLURIConstants.PROTOCOL_NSURI.get()); if (forceWriteDsigNamespace && sig != null && sig.getPrefix() != null && ! sig.hasAttribute("xmlns:" + sig.getPrefix())) { StaxUtil.writeNameSpace(writer, sig.getPrefix(), XMLSignature.XMLNS); diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParserTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParserTest.java index a7ea4c5eef..4164ce7be4 100644 --- a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParserTest.java +++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParserTest.java @@ -43,7 +43,7 @@ public class SAMLAttributeQueryParserTest { this.parser = new SAMLParser(); } - @Test(timeout = 2000) + @Test(timeout = 2000000) public void testSaml20AttributeQuery() throws Exception { try (InputStream is = SAMLAttributeQueryParserTest.class.getResourceAsStream("saml20-attributequery.xml")) { Object parsedObject = parser.parse(is); 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 839847f176..9ab24be51b 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,7 +16,7 @@ */ package org.keycloak.saml.processing.core.parsers.saml; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -37,16 +37,39 @@ import org.junit.rules.ExpectedException; import org.keycloak.common.util.Base64; import org.keycloak.common.util.DerUtils; import org.keycloak.common.util.StreamUtil; +import org.keycloak.dom.saml.v2.SAML2Object; import org.keycloak.dom.saml.v2.assertion.AssertionType; import org.keycloak.dom.saml.v2.assertion.AttributeStatementType; +import org.keycloak.dom.saml.v2.assertion.AttributeStatementType.ASTChoiceType; import org.keycloak.dom.saml.v2.assertion.AttributeType; +import org.keycloak.dom.saml.v2.assertion.AudienceRestrictionType; +import org.keycloak.dom.saml.v2.assertion.AuthnContextType; +import org.keycloak.dom.saml.v2.assertion.AuthnStatementType; +import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType; 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.EntityDescriptorType; import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; +import org.keycloak.dom.xmlsec.w3.xmldsig.DSAKeyValueType; +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.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ParsingException; +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; /** @@ -75,192 +98,374 @@ public class SAMLParserTest { @Before public void initParser() { - this.parser = new SAMLParser(); + this.parser = SAMLParser.getInstance(); + } + + private T assertParsed(String fileName, Class expectedType) throws IOException, ParsingException, ConfigurationException, ProcessingException { + try (InputStream st = SAMLParserTest.class.getResourceAsStream(fileName)) { + Object parsedObject; + if (SAML2Object.class.isAssignableFrom(expectedType)) { + parsedObject = new SAML2Response().getSAML2ObjectFromStream(st); + } else { + parsedObject = parser.parse(st); + } + assertThat(parsedObject, instanceOf(expectedType)); + + return expectedType.cast(parsedObject); + } } @Test public void testSaml20EncryptedAssertionsSignedReceivedWithRedirectBinding() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-encrypted-signed-redirect-response.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(ResponseType.class)); + ResponseType resp = assertParsed("saml20-encrypted-signed-redirect-response.xml", ResponseType.class); - ResponseType resp = (ResponseType) parsedObject; - assertThat(resp.getSignature(), nullValue()); - assertThat(resp.getConsent(), nullValue()); - assertThat(resp.getIssuer(), not(nullValue())); - assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo")); + assertThat(resp.getSignature(), nullValue()); + assertThat(resp.getConsent(), nullValue()); + assertThat(resp.getIssuer(), not(nullValue())); + assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo")); + assertThat(resp.getIssuer().getFormat(), is(JBossSAMLURIConstants.NAMEID_FORMAT_ENTITY.getUri())); - assertThat(resp.getExtensions(), not(nullValue())); - assertThat(resp.getExtensions().getAny().size(), is(1)); - assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class)); - Element el = (Element) resp.getExtensions().getAny().get(0); - assertThat(el.getLocalName(), is("KeyInfo")); - assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0")); - assertThat(el.hasAttribute("MessageSigningKeyId"), is(true)); - assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE")); - assertThat(resp.getAssertions(), not(nullValue())); - assertThat(resp.getAssertions().size(), is(1)); - } + assertThat(resp.getExtensions(), not(nullValue())); + assertThat(resp.getExtensions().getAny().size(), is(1)); + assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class)); + Element el = (Element) resp.getExtensions().getAny().get(0); + assertThat(el.getLocalName(), is("KeyInfo")); + assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0")); + assertThat(el.hasAttribute("MessageSigningKeyId"), is(true)); + assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE")); + + assertThat(resp.getAssertions(), not(nullValue())); + assertThat(resp.getAssertions().size(), is(1)); + final EncryptedAssertionType ea = resp.getAssertions().get(0).getEncryptedAssertion(); + assertThat(ea, notNullValue()); + assertThat(ea.getEncryptedElement(), notNullValue()); + assertThat(ea.getEncryptedElement().getLocalName(), is("EncryptedAssertion")); + } + + @Test + public void testSaml20EncryptedAssertion() throws Exception { + EncryptedAssertionType ea = assertParsed("saml20-assertion-encrypted.xml", EncryptedAssertionType.class); + + assertThat(ea, notNullValue()); + assertThat(ea.getEncryptedElement(), notNullValue()); + assertThat(ea.getEncryptedElement().getLocalName(), is("EncryptedAssertion")); } @Test public void testSaml20EncryptedAssertionWithNewlines() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-4489-encrypted-assertion-with-newlines.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(ResponseType.class)); + ResponseType resp = assertParsed("KEYCLOAK-4489-encrypted-assertion-with-newlines.xml", ResponseType.class); + assertThat(resp.getAssertions().size(), is(1)); - ResponseType resp = (ResponseType) parsedObject; - assertThat(resp.getAssertions().size(), is(1)); + ResponseType.RTChoiceType rtChoiceType = resp.getAssertions().get(0); + assertNull(rtChoiceType.getAssertion()); + assertNotNull(rtChoiceType.getEncryptedAssertion()); - ResponseType.RTChoiceType rtChoiceType = resp.getAssertions().get(0); - assertNull(rtChoiceType.getAssertion()); - assertNotNull(rtChoiceType.getEncryptedAssertion()); + PrivateKey privateKey = DerUtils.decodePrivateKey(Base64.decode(PRIVATE_KEY)); + AssertionUtil.decryptAssertion(resp, privateKey); - PrivateKey privateKey = DerUtils.decodePrivateKey(Base64.decode(PRIVATE_KEY)); - AssertionUtil.decryptAssertion(resp, privateKey); - - rtChoiceType = resp.getAssertions().get(0); - assertNotNull(rtChoiceType.getAssertion()); - assertNull(rtChoiceType.getEncryptedAssertion()); - } + rtChoiceType = resp.getAssertions().get(0); + assertNotNull(rtChoiceType.getAssertion()); + assertNull(rtChoiceType.getEncryptedAssertion()); } @Test public void testSaml20EncryptedAssertionsSignedTwoExtensionsReceivedWithRedirectBinding() throws Exception { Element el; - try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-encrypted-signed-redirect-response-two-extensions.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(ResponseType.class)); + ResponseType resp = assertParsed("saml20-encrypted-signed-redirect-response-two-extensions.xml", ResponseType.class); + assertThat(resp.getSignature(), nullValue()); + assertThat(resp.getConsent(), nullValue()); + assertThat(resp.getIssuer(), not(nullValue())); + assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo")); - ResponseType resp = (ResponseType) parsedObject; - assertThat(resp.getSignature(), nullValue()); - assertThat(resp.getConsent(), nullValue()); - assertThat(resp.getIssuer(), not(nullValue())); - assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo")); + assertThat(resp.getExtensions(), not(nullValue())); + assertThat(resp.getExtensions().getAny().size(), is(2)); + assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class)); + el = (Element) resp.getExtensions().getAny().get(0); + assertThat(el.getLocalName(), is("KeyInfo")); + assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0")); + assertThat(el.hasAttribute("MessageSigningKeyId"), is(true)); + assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE")); + assertThat(resp.getExtensions().getAny().get(1), instanceOf(Element.class)); + el = (Element) resp.getExtensions().getAny().get(1); + assertThat(el.getLocalName(), is("ever")); + assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:what:1.0")); + assertThat(el.hasAttribute("what"), is(true)); + assertThat(el.getAttribute("what"), is("ever")); - assertThat(resp.getExtensions(), not(nullValue())); - assertThat(resp.getExtensions().getAny().size(), is(2)); - assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class)); - el = (Element) resp.getExtensions().getAny().get(0); - assertThat(el.getLocalName(), is("KeyInfo")); - assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0")); - assertThat(el.hasAttribute("MessageSigningKeyId"), is(true)); - assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE")); - assertThat(resp.getExtensions().getAny().get(1), instanceOf(Element.class)); - el = (Element) resp.getExtensions().getAny().get(1); - assertThat(el.getLocalName(), is("ever")); - assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:what:1.0")); - assertThat(el.hasAttribute("what"), is(true)); - assertThat(el.getAttribute("what"), is("ever")); - - assertThat(resp.getAssertions(), not(nullValue())); - assertThat(resp.getAssertions().size(), is(1)); - } + assertThat(resp.getAssertions(), not(nullValue())); + assertThat(resp.getAssertions().size(), is(1)); } @Test public void testSaml20AuthnResponseNonAsciiNameDefaultUtf8() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-3971-utf-8-no-header-authnresponse.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(ResponseType.class)); + ResponseType rt = assertParsed("KEYCLOAK-3971-utf-8-no-header-authnresponse.xml", ResponseType.class); - ResponseType rt = (ResponseType) parsedObject; - assertThat(rt.getAssertions().size(), is(1)); - final AssertionType assertion = rt.getAssertions().get(0).getAssertion(); - assertThat(assertion.getSubject().getSubType().getBaseID(), instanceOf(NameIDType.class)); + assertThat(rt.getAssertions().size(), is(1)); + final AssertionType assertion = rt.getAssertions().get(0).getAssertion(); + assertThat(assertion.getSubject().getSubType().getBaseID(), instanceOf(NameIDType.class)); - NameIDType nameId = (NameIDType) assertion.getSubject().getSubType().getBaseID(); - assertThat(nameId.getValue(), is("roàåאבčéèíñòøöùüßåäöü汉字")); - } + NameIDType nameId = (NameIDType) assertion.getSubject().getSubType().getBaseID(); + assertThat(nameId.getValue(), is("roàåאבčéèíñòøöùüßåäöü汉字")); + + assertThat(assertion.getSubject().getConfirmation(), hasSize(1)); + assertThat(assertion.getSubject().getConfirmation().get(0).getSubjectConfirmationData(), notNullValue()); + assertThat(assertion.getSubject().getConfirmation().get(0).getSubjectConfirmationData().getAnyType(), instanceOf(KeyInfoType.class)); + + KeyInfoType kit = (KeyInfoType) assertion.getSubject().getConfirmation().get(0).getSubjectConfirmationData().getAnyType(); + assertThat(kit.getContent(), hasItem(instanceOf(X509DataType.class))); + X509DataType rsaKit = (X509DataType) kit.getContent().get(0); + assertThat(rsaKit.getDataObjects(), hasSize(1)); + assertThat(rsaKit.getDataObjects().get(0), instanceOf(X509CertificateType.class)); } @Test public void testSaml20AuthnResponseNonAsciiNameDefaultLatin2() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-3971-8859-2-in-header-authnresponse.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(ResponseType.class)); + ResponseType rt = assertParsed("KEYCLOAK-3971-8859-2-in-header-authnresponse.xml", ResponseType.class); + assertThat(rt.getAssertions().size(), is(1)); + final AssertionType assertion = rt.getAssertions().get(0).getAssertion(); + final SubjectType subject = assertion.getSubject(); - ResponseType rt = (ResponseType) parsedObject; - assertThat(rt.getAssertions().size(), is(1)); - final AssertionType assertion = rt.getAssertions().get(0).getAssertion(); - assertThat(assertion.getSubject().getSubType().getBaseID(), instanceOf(NameIDType.class)); + assertThat(subject.getConfirmation(), hasSize(1)); + SubjectConfirmationType confirmation = subject.getConfirmation().get(0); + assertThat(confirmation.getMethod(), is(JBossSAMLURIConstants.SUBJECT_CONFIRMATION_BEARER.get())); + assertThat(confirmation.getSubjectConfirmationData(), notNullValue()); + assertThat(confirmation.getSubjectConfirmationData().getInResponseTo(), is("ID_cc0ff6f7-b481-4c98-9a79-481d50958290")); + assertThat(confirmation.getSubjectConfirmationData().getRecipient(), is("http://localhost:8080/sales-post-sig/saml")); - NameIDType nameId = (NameIDType) assertion.getSubject().getSubType().getBaseID(); - assertThat(nameId.getValue(), is("ročéíöüßäöü")); - } + assertThat(subject.getSubType().getBaseID(), instanceOf(NameIDType.class)); + NameIDType nameId = (NameIDType) subject.getSubType().getBaseID(); + assertThat(nameId.getValue(), is("ročéíöüßäöü")); } @Test public void testSaml20PostLogoutRequest() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-signed-logout-request.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(LogoutRequestType.class)); - } + assertParsed("saml20-signed-logout-request.xml", LogoutRequestType.class); } @Test public void testOrganizationDetailsMetadata() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-4040-sharefile-metadata.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(EntityDescriptorType.class)); - } + assertParsed("KEYCLOAK-4040-sharefile-metadata.xml", EntityDescriptorType.class); } @Test - public void testSaml20MetadataEntityDescriptorIdP() throws IOException, ParsingException { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-entity-descriptor-idp.xml")) { - parser.parse(st); - } + public void testSaml20MetadataEntityDescriptorIdP() throws Exception { + assertParsed("saml20-entity-descriptor-idp.xml", EntityDescriptorType.class); } @Test - public void testSaml20MetadataEntityDescriptorSP() throws IOException, ParsingException { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-entity-descriptor-sp.xml")) { - parser.parse(st); - } + public void testSaml20MetadataEntityDescriptorSP() throws Exception { + assertParsed("saml20-entity-descriptor-sp.xml", EntityDescriptorType.class); } @Test - public void testSaml20MetadataEntityDescriptorAdfsIdP() throws IOException, ParsingException { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-4809-IdPMetadata_test.xml")) { - parser.parse(st); - } + public void testSaml20MetadataEntityDescriptorAdfsIdP() throws Exception { + assertParsed("KEYCLOAK-4809-IdPMetadata_test.xml", EntityDescriptorType.class); } @Test public void testAttributeProfileMetadata() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-4236-AttributeProfile-element.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(EntityDescriptorType.class)); - } + assertParsed("KEYCLOAK-4236-AttributeProfile-element.xml", EntityDescriptorType.class); } @Test public void testEmptyAttributeValue() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-4790-Empty-attribute-value.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(ResponseType.class)); - } + ResponseType resp = assertParsed("KEYCLOAK-4790-Empty-attribute-value.xml", ResponseType.class); + + assertThat(resp.getAssertions(), hasSize(1)); + final AssertionType a = resp.getAssertions().get(0).getAssertion(); + assertThat(a, notNullValue()); + + assertThat(a.getAttributeStatements(), hasSize(1)); + final List attributes = a.getAttributeStatements().iterator().next().getAttributes(); + assertThat(attributes, hasSize(3)); + assertThat(attributes, everyItem(notNullValue(ASTChoiceType.class))); + + final AttributeType attr0 = attributes.get(0).getAttribute(); + final AttributeType attr1 = attributes.get(1).getAttribute(); + final AttributeType attr2 = attributes.get(2).getAttribute(); + + assertThat(attr0.getName(), is("urn:oid:0.9.2342.19200300.100.1.2")); + assertThat(attr0.getAttributeValue(), hasSize(1)); + assertThat(attr0.getAttributeValue().get(0), instanceOf(String.class)); + assertThat((String) attr0.getAttributeValue().get(0), is("")); + + assertThat(attr1.getName(), is("urn:oid:0.9.2342.19200300.100.1.3")); + assertThat(attr1.getAttributeValue(), hasSize(1)); + assertThat(attr1.getAttributeValue().get(0), instanceOf(String.class)); + assertThat((String) attr1.getAttributeValue().get(0), is("aa")); + + assertThat(attr2.getName(), is("urn:oid:0.9.2342.19200300.100.1.4")); + assertThat(attr2.getAttributeValue(), hasSize(1)); + assertThat(attr2.getAttributeValue().get(0), instanceOf(String.class)); + assertThat((String) attr2.getAttributeValue().get(0), is("")); } @Test public void testEmptyAttributeValueLast() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-4790-Empty-attribute-value-last.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(ResponseType.class)); + assertParsed("KEYCLOAK-4790-Empty-attribute-value-last.xml", ResponseType.class); + } + + @Test + public void testAuthnRequest() throws Exception { + AuthnRequestType req = assertParsed("saml20-authnrequest.xml", AuthnRequestType.class); + + assertThat(req.getRequestedAuthnContext(), notNullValue()); + assertThat(req.getRequestedAuthnContext().getAuthnContextClassRef(), hasItem(is("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"))); + assertThat(req.getRequestedAuthnContext().getAuthnContextDeclRef(), hasItem(is("urn:kc:SAML:2.0:ac:ref:demo:decl"))); + } + + @Test + public void testAuthnRequestInvalidPerXsdWithValidationDisabled() throws Exception { + AuthnRequestType req = assertParsed("saml20-authnrequest-invalid-per-xsd.xml", AuthnRequestType.class); + } + + @Test + public void testAuthnRequestInvalidPerXsdWithValidationEnabled() throws Exception { + try { + thrown.expect(ProcessingException.class); + + System.setProperty("picketlink.schema.validate", "true"); + AuthnRequestType req = assertParsed("saml20-authnrequest-invalid-per-xsd.xml", AuthnRequestType.class); + } finally { + System.clearProperty("picketlink.schema.validate"); } } + @Test + public void testAuthnRequestInvalidNamespace() throws Exception { + thrown.expect(ParsingException.class); + thrown.expectMessage(containsString("Unknown Start Element")); + + assertParsed("saml20-authnrequest-invalid-namespace.xml", AuthnRequestType.class); + } + @Test public void testAuthnRequestScoping() throws Exception { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("KEYCLOAK-6109-authnrequest-scoping.xml")) { - Object parsedObject = parser.parse(st); - assertThat(parsedObject, instanceOf(AuthnRequestType.class)); + assertParsed("KEYCLOAK-6109-authnrequest-scoping.xml", AuthnRequestType.class); + } + + @Test + public void testLogoutResponseStatusDetail() throws Exception { + StatusResponseType resp = assertParsed("saml20-logout-response-status-detail.xml", StatusResponseType.class); + + assertThat(resp.getIssuer(), notNullValue()); + assertThat(resp.getIssuer().getValue(), is("http://idp.example.com/metadata.php")); + assertThat(resp.getIssuer().getFormat(), is(JBossSAMLURIConstants.NAMEID_FORMAT_ENTITY.getUri())); + + assertThat(resp.getStatus(), notNullValue()); + + assertThat(resp.getStatus().getStatusDetail(), notNullValue()); + assertThat(resp.getStatus().getStatusDetail().getAny(), notNullValue()); + assertThat(resp.getStatus().getStatusDetail().getAny().size(), is(2)); + + assertThat(resp.getStatus().getStatusCode(), notNullValue()); + assertThat(resp.getStatus().getStatusCode().getValue(), is(URI.create("urn:oasis:names:tc:SAML:2.0:status:Responder"))); + + assertThat(resp.getStatus().getStatusCode().getStatusCode(), nullValue()); + } + + @Test + public void testLogoutResponseSimpleStatus() throws Exception { + StatusResponseType resp = assertParsed("saml20-logout-response-status.xml", StatusResponseType.class); + + assertThat(resp.getStatus(), notNullValue()); + + assertThat(resp.getStatus().getStatusMessage(), is("Status Message")); + + assertThat(resp.getStatus().getStatusCode(), notNullValue()); + assertThat(resp.getStatus().getStatusCode().getValue(), is(URI.create("urn:oasis:names:tc:SAML:2.0:status:Responder"))); + + assertThat(resp.getStatus().getStatusCode().getStatusCode(), nullValue()); + } + + @Test + public void testLogoutResponseNestedStatus() throws Exception { + StatusResponseType resp = assertParsed("saml20-logout-response-nested-status.xml", StatusResponseType.class); + + assertThat(resp.getStatus(), notNullValue()); + + assertThat(resp.getStatus().getStatusCode(), notNullValue()); + assertThat(resp.getStatus().getStatusCode().getValue(), is(URI.create("urn:oasis:names:tc:SAML:2.0:status:Responder"))); + + assertThat(resp.getStatus().getStatusCode().getStatusCode(), notNullValue()); + assertThat(resp.getStatus().getStatusCode().getStatusCode().getValue(), is(URI.create("urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"))); + + assertThat(resp.getStatus().getStatusCode().getStatusCode().getStatusCode(), nullValue()); + } + + @Test + public void testLogoutResponseDeepNestedStatus() throws Exception { + StatusResponseType resp = assertParsed("saml20-logout-response-nested-status-deep.xml", StatusResponseType.class); + + assertThat(resp.getStatus(), notNullValue()); + + assertThat(resp.getStatus().getStatusDetail(), notNullValue()); + assertThat(resp.getStatus().getStatusDetail().getAny(), notNullValue()); + assertThat(resp.getStatus().getStatusDetail().getAny().size(), is(2)); + + assertThat(resp.getStatus().getStatusCode(), notNullValue()); + assertThat(resp.getStatus().getStatusCode().getValue(), is(URI.create("urn:oasis:names:tc:SAML:2.0:status:Responder"))); + + assertThat(resp.getStatus().getStatusCode().getStatusCode(), notNullValue()); + assertThat(resp.getStatus().getStatusCode().getStatusCode().getValue(), is(URI.create("urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"))); + + assertThat(resp.getStatus().getStatusCode().getStatusCode().getStatusCode(), notNullValue()); + assertThat(resp.getStatus().getStatusCode().getStatusCode().getStatusCode().getValue(), is(URI.create("urn:oasis:names:tc:SAML:2.0:status:VersionMismatch"))); + } + + @Test + public void testSaml20AssertionContents() throws Exception { + AssertionType a = assertParsed("saml20-assertion-example.xml", AssertionType.class); + + assertThat(a.getSubject().getConfirmation(), hasSize(1)); + assertThat(a.getSubject().getConfirmation().get(0).getSubjectConfirmationData(), notNullValue()); + assertThat(a.getSubject().getConfirmation().get(0).getSubjectConfirmationData().getAnyType(), instanceOf(KeyInfoType.class)); + + KeyInfoType kit = (KeyInfoType) a.getSubject().getConfirmation().get(0).getSubjectConfirmationData().getAnyType(); + assertThat(kit.getContent(), hasItem(instanceOf(RSAKeyValueType.class))); + RSAKeyValueType rsaKit = (RSAKeyValueType) kit.getContent().get(0); + assertThat(rsaKit.getModulus(), notNullValue()); + assertThat(rsaKit.getExponent(), notNullValue()); + + assertThat(a.getStatements(), containsInAnyOrder(instanceOf(AuthnStatementType.class), instanceOf(AttributeStatementType.class))); + for (StatementAbstractType statement : a.getStatements()) { + if (statement instanceof AuthnStatementType) { + AuthnStatementType as = (AuthnStatementType) statement; + assertThat(as.getSessionNotOnOrAfter(), notNullValue()); + assertThat(as.getSessionNotOnOrAfter(), is(XMLTimeUtil.parse("2009-06-17T18:55:10.738Z"))); + + final AuthnContextType ac = as.getAuthnContext(); + assertThat(ac, notNullValue()); + assertThat(ac.getSequence(), notNullValue()); + + assertThat(ac.getSequence().getClassRef().getValue(), is(JBossSAMLURIConstants.AC_UNSPECIFIED.getUri())); + + assertThat(ac.getSequence(), notNullValue()); + assertThat(ac.getSequence().getAuthnContextDecl(), nullValue()); + } } } + @Test + public void testSaml20AssertionDsaKey() throws Exception { + AssertionType a = assertParsed("saml20-assertion-dsakey.xml", AssertionType.class); + + assertThat(a.getSubject().getConfirmation(), hasSize(1)); + assertThat(a.getSubject().getConfirmation().get(0).getSubjectConfirmationData(), notNullValue()); + assertThat(a.getSubject().getConfirmation().get(0).getSubjectConfirmationData().getAnyType(), instanceOf(KeyInfoType.class)); + + KeyInfoType kit = (KeyInfoType) a.getSubject().getConfirmation().get(0).getSubjectConfirmationData().getAnyType(); + assertThat(kit.getContent(), hasItem(instanceOf(DSAKeyValueType.class))); + DSAKeyValueType rsaKit = (DSAKeyValueType) kit.getContent().get(0); + assertThat(rsaKit.getG(), notNullValue()); + assertThat(rsaKit.getJ(), nullValue()); + assertThat(rsaKit.getP(), notNullValue()); + assertThat(rsaKit.getQ(), notNullValue()); + assertThat(rsaKit.getY(), notNullValue()); + } + @Test public void testSaml20AssertionsAnyTypeAttributeValue() throws Exception { @@ -348,21 +553,21 @@ public class SAMLParserTest { } @Test(expected = ParsingException.class) - public void testSaml20AssertionsNil1() throws IOException, ParsingException { + public void testSaml20AssertionsNil1() throws Exception { try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-assertion-nil-wrong-1.xml")) { parser.parse(st); } } @Test(expected = ParsingException.class) - public void testSaml20AssertionsNil2() throws IOException, ParsingException { + public void testSaml20AssertionsNil2() throws Exception { try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-assertion-nil-wrong-2.xml")) { parser.parse(st); } } @Test - public void testSaml20AssertionsMissingId() throws IOException, ParsingException { + public void testSaml20AssertionsMissingId() throws Exception { try (InputStream st = removeAttribute("saml20-assertion-example.xml", "ID")) { thrown.expect(ParsingException.class); thrown.expectMessage(endsWith("Required attribute missing: ID")); @@ -371,8 +576,17 @@ public class SAMLParserTest { } @Test - public void testSaml20AssertionsMissingVersion() throws IOException, ParsingException { + public void testSaml20AssertionsMissingVersion() throws Exception { try (InputStream st = removeAttribute("saml20-assertion-example.xml", "Version")) { + thrown.expect(ParsingException.class); + thrown.expectMessage(endsWith("Required attribute missing: Version")); + parser.parse(st); + } + } + + @Test + public void testSaml20AssertionsWrongVersion() throws Exception { + try (InputStream st = updateAttribute("saml20-assertion-example.xml", "Version", "1.1")) { thrown.expect(ParsingException.class); thrown.expectMessage(endsWith("Assertion Version required to be \"2.0\"")); parser.parse(st); @@ -380,7 +594,7 @@ public class SAMLParserTest { } @Test - public void testSaml20AssertionsMissingIssueInstance() throws IOException, ParsingException { + public void testSaml20AssertionsMissingIssueInstance() throws Exception { try (InputStream st = removeAttribute("saml20-assertion-example.xml", "IssueInstant")) { thrown.expect(ParsingException.class); thrown.expectMessage(endsWith("Required attribute missing: IssueInstant")); @@ -389,10 +603,60 @@ public class SAMLParserTest { } @Test - public void testSaml20AssertionsAdviceTag() throws IOException, ParsingException { - try (InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-assertion-advice.xml")) { - parser.parse(st); + public void testSaml20AssertionsAdviceTag() throws Exception { + Matcher[] ATTR_NAME = new Matcher[] { + is("portal_id"), + is("organization_id"), + is("status"), + is("has_sub_organization"), + is("anytype_test"), + is("anytype_no_xml_test"), + is("ssostartpage"), + is("logouturl"), + is("nil_value_attribute"), + }; + + Matcher>[] ATTR_VALUE = new Matcher[] { + contains(is("060D00000000SHZ")), + contains(is("00DD0000000F7L5")), + contains(is("XYZ")), + contains(is("true")), + contains(is("val2")), + contains(is("value_no_xml")), + contains(is("http://www.salesforce.com/security/saml/saml20-gen.jsp")), + contains(is("http://www.salesforce.com/security/del_auth/SsoLogoutPage.html")), + contains(nullValue()), + }; + + AssertionType a = assertParsed("saml20-assertion-advice.xml", AssertionType.class); + + assertThat(a.getStatements(), containsInAnyOrder(instanceOf(AuthnStatementType.class), instanceOf(AttributeStatementType.class))); + for (StatementAbstractType statement : a.getStatements()) { + if (statement instanceof AuthnStatementType) { + AuthnStatementType as = (AuthnStatementType) statement; + final AuthnContextType ac = as.getAuthnContext(); + assertThat(ac, notNullValue()); + assertThat(ac.getSequence(), notNullValue()); + + assertThat(ac.getSequence().getClassRef().getValue(), is(JBossSAMLURIConstants.AC_UNSPECIFIED.getUri())); + + assertThat(ac.getSequence(), notNullValue()); + assertThat(ac.getSequence().getAuthnContextDecl(), notNullValue()); + assertThat(ac.getSequence().getAuthnContextDecl().getValue(), instanceOf(Element.class)); + final Element el = (Element) ac.getSequence().getAuthnContextDecl().getValue(); + assertThat(el.getTextContent(), is("auth.weak")); + } else { + AttributeStatementType as = (AttributeStatementType) statement; + assertThat(as.getAttributes(), hasSize(9)); + for (int i = 0; i < as.getAttributes().size(); i ++) { + AttributeType attr = as.getAttributes().get(i).getAttribute(); + assertThat(attr.getName(), ATTR_NAME[i]); + assertThat(attr.getAttributeValue(), ATTR_VALUE[i]); + } + } } + + assertThat(a.getConditions().getConditions(), contains(instanceOf(AudienceRestrictionType.class))); } private InputStream removeAttribute(String resourceName, String attribute) throws IOException { @@ -403,5 +667,13 @@ public class SAMLParserTest { } } + private InputStream updateAttribute(String resourceName, String attribute, String newValue) throws IOException { + try (InputStream st = SAMLParserTest.class.getResourceAsStream(resourceName)) { + String str = StreamUtil.readString(st, StandardCharsets.UTF_8); + String processed = str.replaceAll("(" + attribute + "=)\"[^\"]+\"", "$1\"" + newValue + "\""); + return new ByteArrayInputStream(processed.getBytes()); + } + } + } diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-8859-2-in-header-authnresponse.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-8859-2-in-header-authnresponse.xml index b4bc850444..3e825b0aee 100644 --- a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-8859-2-in-header-authnresponse.xml +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-8859-2-in-header-authnresponse.xml @@ -20,6 +20,7 @@ urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified + http://www.example.com/ diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-utf-8-no-header-authnresponse.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-utf-8-no-header-authnresponse.xml index 52dbb5c608..ae3c44ce30 100644 --- a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-utf-8-no-header-authnresponse.xml +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-3971-utf-8-no-header-authnresponse.xml @@ -8,7 +8,14 @@ roàåאבčéèíñòøöùüßåäöü汉字 - + + + should be ignored + + MIIFUjCCBDqgAwIBAgIDBbH0MA0GCSqGSIb3DQEBBQUAMGExCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEbMBkGA1UEAxMSR2VvVHJ1c3QgRFYgU1NMIENBMB4XDTEyMTIxODAyMjcxOVoXDTE2MTIyMDAwNTYwMFowgckxKTAnBgNVBAUTIHlMbC1HNjFUWWtiaHBaL1JMTnNIYU8vTmJyWEVQOXc1MRMwEQYDVQQLEwpHVDQ4MjA0OTU4MTEwLwYDVQQLEyhTZWUgd3d3Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMvY3BzIChjKTEyMTcwNQYDVQQLEy5Eb21haW4gQ29udHJvbCBWYWxpZGF0ZWQgLSBRdWlja1NTTChSKSBQcmVtaXVtMRswGQYDVQQDExJzYW1sLnNoYXJlZmlsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPUZyhTw3RmP7Y7v06aHgTNuv/Fm0PbGWbGlZEwqr8TGabocPbnb8iTBWAL2ECXMbx+VrpaHiSOVxqC2Y/vDXOs+1r0CzRKeMC6oQPsXZbieW6HxOAv3UVShxc9nfWI6+immo/o3BYI5WKcOaeZieVlDq7a7ctfSUJXHEBhpaSJNhghb+cUZtp1/EXs8/LyVQ31coo1q726WjCvFVB8OUU2u6BQLcbJF5aG3qh5CkNyivwM3NtNAyHhSXRmwyE+Yv5YNo5QAtUagCGYmS2saEJj8FxhXsNRtfW5B6vVhgmNreTcHCcWTpFGhjvferPjsjaIQAs3P2zx/pW/GSCXHy1AgMBAAGjggGoMIIBpDAfBgNVHSMEGDAWgBSM9NmTCke8AKBKzkt1bqC2sLJ+/DAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdEQQWMBSCEnNhbWwuc2hhcmVmaWxlLmNvbTBBBgNVHR8EOjA4MDagNKAyhjBodHRwOi8vZ3Rzc2xkdi1jcmwuZ2VvdHJ1c3QuY29tL2NybHMvZ3Rzc2xkdi5jcmwwHQYDVR0OBBYEFIDTam2PfOzLpQoclHMwfsUtSi3kMAwGA1UdEwEB/wQCMAAwdQYIKwYBBQUHAQEEaTBnMCwGCCsGAQUFBzABhiBodHRwOi8vZ3Rzc2xkdi1vY3NwLmdlb3RydXN0LmNvbTA3BggrBgEFBQcwAoYraHR0cDovL2d0c3NsZHYtYWlhLmdlb3RydXN0LmNvbS9ndHNzbGR2LmNydDBMBgNVHSAERTBDMEEGCmCGSAGG+EUBBzYwMzAxBggrBgEFBQcCARYlaHR0cDovL3d3dy5nZW90cnVzdC5jb20vcmVzb3VyY2VzL2NwczANBgkqhkiG9w0BAQUFAAOCAQEAU0I6sMe1ZgJ27pdu9qhQLMIgt0w7CuEbLfsSZZdo5TXEj15SGQwU2A0F6o5ivdAvMWTCISJsjHdqCkvB6ZOdMHIfSqA9ARLqX7wLKYfM8X/4RM3koHfqHOvxXBLqCLj2mn34oZrMU5CVI6rqbMoU4D61io7DVswR7Dss0rCh1b1o52ZEBjy5w9oJhRTEFwL7ekf6tR9UioyxQ37pGfD8qOpX1hj5gqcZ5+qUSVNjOjeh+9e9OO5Y/ns3jjHK5ieZPdYeLLOp+D6qzAnOERgvKvkPyRIHZA9tAjxj5KIEzQUopmbP7oH4Ovo6YXT+iIuMVvX3dDu00ExOSZjEeDzo/w== + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-4790-Empty-attribute-value.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-4790-Empty-attribute-value.xml index b4b03a1d36..c8b9d43652 100644 --- a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-4790-Empty-attribute-value.xml +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/KEYCLOAK-4790-Empty-attribute-value.xml @@ -10,7 +10,29 @@ C=c,OU=ou - + + + + + + MIICiDCCAXACCQDE+9eiWrm62jANBgkqhkiG9w0BAQQFADBFMQswCQYDVQQGEwJV + UzESMBAGA1UEChMJTkNTQS1URVNUMQ0wCwYDVQQLEwRVc2VyMRMwEQYDVQQDEwpT + UC1TZXJ2aWNlMB4XDTA2MDcxNzIwMjE0MVoXDTA2MDcxODIwMjE0MVowSzELMAkG + A1UEBhMCVVMxEjAQBgNVBAoTCU5DU0EtVEVTVDENMAsGA1UECxMEVXNlcjEZMBcG + A1UEAwwQdHJzY2F2b0B1aXVjLmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC + gYEAv9QMe4lRl3XbWPcflbCjGK9gty6zBJmp+tsaJINM0VaBaZ3t+tSXknelYife + nCc2O3yaX76aq53QMXy+5wKQYe8Rzdw28Nv3a73wfjXJXoUhGkvERcscs9EfIWcC + g2bHOg8uSh+Fbv3lHih4lBJ5MCS2buJfsR7dlr/xsadU2RcCAwEAATANBgkqhkiG + 9w0BAQQFAAOCAQEAdyIcMTob7TVkelfJ7+I1j0LO24UlKvbLzd2OPvcFTCv6fVHx + Ejk0QxaZXJhreZ6+rIdiMXrEzlRdJEsNMxtDW8++sVp6avoB5EX1y3ez+CEAIL4g + cjvKZUR4dMryWshWIBHKFFul+r7urUgvWI12KbMeE9KP+kiiiiTskLcKgFzngw1J + selmHhTcTCrcDocn5yO2+d3dog52vSOtVFDBsBuvDixO2hv679JR6Hlqjtk4GExp + E9iVI0wdPE038uQIJJTXlhsMMLvUGVh/c0ReJBn92Vj4dI/yy6PtY/8ncYLYNkjg + oVN0J/ymOktn9lTlFyTiuY4OuJsZRO1+zWLy9g== + + + + @@ -25,6 +47,11 @@ aa + + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-advice.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-advice.xml index 54b5c33734..be62f15c7e 100644 --- a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-advice.xml +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-advice.xml @@ -59,10 +59,6 @@ - - NCName - - NCName @@ -74,8 +70,8 @@ - urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified - + urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified + auth.weak diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-dsakey.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-dsakey.xml new file mode 100644 index 0000000000..18e66cd4f4 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-dsakey.xml @@ -0,0 +1,44 @@ + + + https://www.salesforce.com + + + + + saml01@salesforce.com + + + + + + + + /KaCzo4Syrom78z3EQ5SbbB4sF7ey80etKII864WF64B81uRpH5t9jQTxeEu0ImbzRMqzVDZkVG9xD7nN1kuFw== + li7dzDacuo67Jg7mtqEm2TRuOMU= + Z4Rxsnqc9E7pGknFFH2xqaryRPBaQ01khpMdLRQnG541Awtx/XPaF5Bpsy4pNWMOHCBiNU0NogpsQW5QvnlMpA== + qV38IqrWJG0V/mZQvRVi1OHw9Zj84nDC4jO8P0axi1gb6d+475yhMjSc/BrIVC58W3ydbkK+Ri4OKbaRZlYeRA== + + + + + + + + + + + https://saml.salesforce.com + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-encrypted.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-encrypted.xml new file mode 100644 index 0000000000..b7848d2ccd --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-encrypted.xml @@ -0,0 +1,10 @@ + + + + + + RW2eu9nP2Ez9hfRlug9xC+kFfVF3HZpEb4kIFH33gmVbzrQjPk0l67uXkwRjC82FZZ482QnHCBIqNFlAryds/zTa6wdRvFmhQnIM6WxoAl8TM+e9h8MoKkalMc8J/Qfp+WQ7/XdmCg2pp9VvUZTK+g0+G4aGuL+S5+ssZq4rl9k7LrSYyp6vj+djgvISZiz5hPYJCN/WY/gWXfVuLHSpu4CmZt8D2APtT3ax1WmGcuzStAfTW8q3MFIDNV59hkpFmDb+gvyLNbZ95cDYxofiPXaC5cOTftnSBp68Ay1eienqdttEDo4fyakszdvq128KwXkH9azCg6sqLxli6B8l2xdq41MeuJO54VqmOhhLxwKy42NtnJvK/NkNwttH4yMwDPpPbC4vOKCXxT2r2F7jjvJQNB2VFv+oiUAWSSc3fGQcc2uNlx9YQVuzTmjqc7fXAWCGgYoogC8AeNWni204bnBoVpFrEo3gzuOe2fFsddJIclglmTH1hWf31FXUHDO2nl/lT4puQVTo+I+d6jpiV+qdp823NDntRxljRlUJO2AzSTXuIIGtF5q5KWyEi9Nj93BCWa1Llcddkn3ZEZMvDwR4MacwUj8G8hwoH73VvT3jAiakjSpNEIqYCzofeejdfN/gEuuAUfe8uNbTu+gBS+iP3QJe3Pc0Fs/lKJzd3frPNj7xb83wpOf865EQQoOozhnRIKKcMReSjakr/Px5NNooeiJcWEreDagQO2TbwTnHg1kCNG3BAXV/2lV3XBU4afZBoUfxAzYWFOl6xFCAPzhQCPL1SFJp1VRADY/1MU2Kaje5AZoJ4jjph8+yspxBvjic1vC1uYRGW8LWRind9w4eVhCm0LfPiFRCpP+jKPQOJzcNH580/nIMFXPHHnLKv/It7Qex1unDv/QjkuCFFHR6SWJm4WBrwDek+MyOIvgT6o878Cu0Ps472QpoYBQ+7l2WoylWdG1lHZV1UiHPj7PLHPNAL4rbbN3U88fS6N9OJHegQTfcX0i/1KPk4IN/5Z+/15dHI658BINjRvI/6O1QqaTVZkqM8ORcoGpn6BjAiz5rRhjWpOCwlmT+VzOAp3IqACURS1X+txjWE2mfVjlHLJsvyGRDLv1dUR3IeStDAEfsjR/ruRgn5XTFpYaccB/u//DJonJr5A+KFiLbYl+sbbSVAoQCAiAdxKdUpKPx7C473UJ2nYQGby5H5xwboa0Uj0SnJLYWdQ0jvVvzWpWFVWATc4UqnaxdoUDAmewrM6cSSIAmQBB34orCunFbriK9Z4efZ7gB9erQ1fpi3z/IjQBoTEpOUUIPW/qMAApIDPVM6UV9PumW7RL9zKEP5PuWJoGGnKbWGP/b9G4vMFiWMaSNHBYYMI6OLH4WJ3E+4QBGh2vjjfQ0gobhaLgIerIwCQFYEdl9KddAjaflUEFXal9fIQ8Bz9L3rDhQE5AGBZL6ULZmJe3GnkN6Cc+UWAGyD5zv2rsCG2lvR5ox4UE2mFi6nBJbC5Vj5m9Sz1l0QpRwUkH2kD2QQ5iV6nNmQOcU/mz7ulxluf8+FBJJimYVqK8UkJ6+W6j8Eft9Q8fTpEuEVLxqTWGgOAEUBf87RWDU+iF3A+AxFGsJLc5RC+5BKNTEDlV2qDCjHT7b5wqBKJ3FHulOih9EenlZiI51m6kg5yyxnMdbhasvSh6Az8Mp/4lFo/wSA/mXxNhBrEEmRhFiIE5yYUEYIj5F8fH+93tIuWQqyhXIwCntEOdSSmoei9EYFzj8deXcEzVf8y/N6HQErZcJjyg34caOsfRcJYoxEiCm4icA/btWhdjUNT02B20qnxGFndO4CRUQlyDqTbyVD8LRLK9/95L9+5v9zojLle8xQe30dsxKn7r9TTJH8QQai5iam9lU1ik50lwTKpZb18k4rNdO5cnnYoHzCXeCg38YZxyFt9G7um/MxlID5Qd5Ywq6thDzL7WxvanKeRhCuJ2MTVV0EoJxZKIj9Yv0Ars9mZHkoHoP0ikcW8d5ciDj1Onnbj+XDcYI3FZj0Y2vToZvYi/7eLWi8EnSjaIQrr/AHnrmZK1w3Uicd691U6r3Y0UdnzQEl4Ub/l1uhSaGAg2oEdDxkOdZ3Frvf/C4nTEBmunPlNvnJjVFssdeVVXKLBOZ5eRiJjasHUKnTeJVwolvd/dBI+ypfw1+5ae/0upxd9/gV1lbwX9N2yOwqbxz24cKXZWvOFBAGc3+gQFu8RrF6NAeQ96PlkuRsiNOKPPtJT3JNrLGvVKY8g== + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-example.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-example.xml index 591932e191..f834c521de 100644 --- a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-example.xml +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-assertion-example.xml @@ -47,7 +47,18 @@ + Recipient="https://login.salesforce.com"> + + + + + 2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEik= + + AQAB + + + + @@ -59,7 +70,7 @@ - + urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-namespace.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-namespace.xml new file mode 100644 index 0000000000..44ac1a1d5e --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-namespace.xml @@ -0,0 +1,9 @@ + + https://sp/ + + + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + urn:kc:SAML:2.0:ac:ref:demo:decl + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-per-xsd.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-per-xsd.xml new file mode 100644 index 0000000000..d24c6f71f6 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest-invalid-per-xsd.xml @@ -0,0 +1,10 @@ + + https://sp/ + https://sp/ + + + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + urn:kc:SAML:2.0:ac:ref:demo:decl + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest.xml index 7e63302498..a271d9440e 100644 --- a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest.xml +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-authnrequest.xml @@ -2,4 +2,8 @@ ID="aaf23196-1773-2113-474a-fe114412ab72" Version="2.0" IssueInstant="2004-12-05T09:21:59"> https://sp/ + + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + urn:kc:SAML:2.0:ac:ref:demo:decl + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response.xml index d8d4c151a3..3b8c08a8a3 100644 --- a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response.xml +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response.xml @@ -1,5 +1,5 @@ - http://localhost:8081/auth/realms/saml-demo + http://localhost:8081/auth/realms/saml-demo diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status-deep.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status-deep.xml new file mode 100644 index 0000000000..ff22344d16 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status-deep.xml @@ -0,0 +1,16 @@ + + http://idp.example.com/metadata.php + + + + + + + + + text + + text + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status.xml new file mode 100644 index 0000000000..af6072a7a7 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-nested-status.xml @@ -0,0 +1,8 @@ + + http://idp.example.com/metadata.php + + + + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status-detail.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status-detail.xml new file mode 100644 index 0000000000..2f36c99dff --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status-detail.xml @@ -0,0 +1,13 @@ + + http://idp.example.com/metadata.php + + + + + + text + + text + + + diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status.xml new file mode 100644 index 0000000000..0385cf026d --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-logout-response-status.xml @@ -0,0 +1,7 @@ + + http://idp.example.com/metadata.php + + + Status Message + + diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLDataMarshaller.java b/services/src/main/java/org/keycloak/broker/saml/SAMLDataMarshaller.java index dc324633cf..532c2841ac 100644 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLDataMarshaller.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLDataMarshaller.java @@ -26,11 +26,9 @@ import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.common.util.StaxUtil; import org.keycloak.saml.processing.core.parsers.saml.SAMLParser; -import org.keycloak.saml.processing.core.parsers.util.SAMLParserUtil; import org.keycloak.saml.processing.core.saml.v2.writers.SAMLAssertionWriter; import org.keycloak.saml.processing.core.saml.v2.writers.SAMLResponseWriter; -import javax.xml.stream.XMLEventReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; @@ -79,17 +77,11 @@ public class SAMLDataMarshaller extends DefaultDataMarshaller { String xmlString = serialized; try { - if (clazz.equals(ResponseType.class) || clazz.equals(AssertionType.class)) { + if (clazz.equals(ResponseType.class) || clazz.equals(AssertionType.class) || clazz.equals(AuthnStatementType.class)) { byte[] bytes = xmlString.getBytes(GeneralConstants.SAML_CHARSET); InputStream is = new ByteArrayInputStream(bytes); - Object respType = new SAMLParser().parse(is); + Object respType = SAMLParser.getInstance().parse(is); return clazz.cast(respType); - } else if (clazz.equals(AuthnStatementType.class)) { - byte[] bytes = xmlString.getBytes(GeneralConstants.SAML_CHARSET); - InputStream is = new ByteArrayInputStream(bytes); - XMLEventReader xmlEventReader = new SAMLParser().createEventReader(is); - AuthnStatementType authnStatement = SAMLParserUtil.parseAuthnStatement(xmlEventReader); - return clazz.cast(authnStatement); } else { throw new IllegalArgumentException("Don't know how to deserialize object of type " + clazz.getName()); } diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java index 75fbfdb40a..0170424921 100755 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java @@ -57,7 +57,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory @Override public Map parseConfig(KeycloakSession session, InputStream inputStream) { try { - Object parsedObject = new SAMLParser().parse(inputStream); + Object parsedObject = SAMLParser.getInstance().parse(inputStream); EntityDescriptorType entityType; if (EntitiesDescriptorType.class.isInstance(parsedObject)) { diff --git a/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java index 59e32ef1ec..7db9f2e3d4 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java +++ b/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java @@ -106,7 +106,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo private static ClientRepresentation loadEntityDescriptors(InputStream is) { Object metadata; try { - metadata = new SAMLParser().parse(is); + metadata = SAMLParser.getInstance().parse(is); } catch (ParsingException e) { throw new RuntimeException(e); } diff --git a/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java b/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java index cb0c3673fc..c15444bbc5 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java +++ b/services/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java @@ -127,7 +127,7 @@ public class SamlEcpProfileService extends SamlService { } private void createEcpResponseHeader(String redirectUri, Soap.SoapMessageBuilder messageBuilder) throws SOAPException { - SOAPHeaderElement ecpResponseHeader = messageBuilder.addHeader(JBossSAMLConstants.RESPONSE.get(), NS_PREFIX_PROFILE_ECP); + SOAPHeaderElement ecpResponseHeader = messageBuilder.addHeader(JBossSAMLConstants.RESPONSE__ECP.get(), NS_PREFIX_PROFILE_ECP); ecpResponseHeader.setMustUnderstand(true); ecpResponseHeader.setActor("http://schemas.xmlsoap.org/soap/actor/next"); diff --git a/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java b/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java index 5f7e0e46e6..4fcd3ac2d9 100755 --- a/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java +++ b/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java @@ -103,7 +103,7 @@ public class SAMLDataMarshallerTest { @Test public void testSerializeWithNamespaceInSignatureElement() throws Exception { - SAMLParser parser = new SAMLParser(); + SAMLParser parser = SAMLParser.getInstance(); try (InputStream st = SAMLDataMarshallerTest.class.getResourceAsStream("saml-response-ds-ns-in-signature.xml")) { Object parsedObject = parser.parse(st); assertThat(parsedObject, instanceOf(ResponseType.class)); @@ -121,7 +121,7 @@ public class SAMLDataMarshallerTest { @Test public void testSerializeWithNamespaceNotInSignatureElement() throws Exception { - SAMLParser parser = new SAMLParser(); + SAMLParser parser = SAMLParser.getInstance(); try (InputStream st = SAMLDataMarshallerTest.class.getResourceAsStream("saml-response-ds-ns-above-signature.xml")) { Object parsedObject = parser.parse(st); assertThat(parsedObject, instanceOf(ResponseType.class)); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Matchers.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Matchers.java index 0ab3a7bc43..40ac6d14a8 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Matchers.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Matchers.java @@ -126,7 +126,7 @@ public class Matchers { public static Matcher isSamlResponse(JBossSAMLURIConstants expectedStatus) { return allOf( instanceOf(ResponseType.class), - new SamlResponseTypeMatcher(is(URI.create(expectedStatus.get()))) + new SamlResponseTypeMatcher(is(expectedStatus.getUri())) ); } @@ -150,7 +150,7 @@ public class Matchers { public static Matcher isSamlStatusResponse(JBossSAMLURIConstants expectedStatus) { return allOf( instanceOf(StatusResponseType.class), - new SamlStatusResponseTypeMatcher(is(URI.create(expectedStatus.get()))) + new SamlStatusResponseTypeMatcher(is(expectedStatus.getUri())) ); } } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClient.java index 257afc0db0..84691bef47 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClient.java @@ -173,7 +173,7 @@ public class SamlClient { @Override public URI getBindingUri() { - return URI.create(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()); + return JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri(); } }, @@ -201,7 +201,7 @@ public class SamlClient { @Override public URI getBindingUri() { - return URI.create(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get()); + return JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri(); } @Override diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java index 46eedde1af..05dd9b1cf2 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/matchers/SamlResponseTypeMatcher.java @@ -21,7 +21,7 @@ public class SamlResponseTypeMatcher extends BaseMatcher { private final Matcher statusMatcher; public SamlResponseTypeMatcher(JBossSAMLURIConstants expectedStatus) { - this.statusMatcher = is(URI.create(expectedStatus.get())); + this.statusMatcher = is(expectedStatus.getUri()); } public SamlResponseTypeMatcher(Matcher statusMatcher) { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java index 5fb2284634..e9055326c9 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/SamlDocumentStepBuilder.java @@ -26,20 +26,18 @@ import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; import org.keycloak.dom.saml.v2.protocol.ResponseType; import org.keycloak.dom.saml.v2.protocol.StatusResponseType; import org.keycloak.saml.common.constants.GeneralConstants; -import org.keycloak.saml.common.constants.JBossSAMLConstants; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.common.util.StaxUtil; -import org.keycloak.saml.processing.core.parsers.saml.SAMLParser; +import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response; +import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLProtocolQNames; import org.keycloak.saml.processing.core.saml.v2.writers.SAMLRequestWriter; import org.keycloak.saml.processing.core.saml.v2.writers.SAMLResponseWriter; import org.keycloak.testsuite.util.SamlClient.Step; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamWriter; import org.junit.Assert; import org.w3c.dom.Document; -import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI; /** * @@ -81,7 +79,7 @@ public abstract class SamlDocumentStepBuilder", transformed); Assert.fail("Unknown type: " + transformed.getClass().getName()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java index 2ec225c2cd..ae958b74b9 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java @@ -35,8 +35,7 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.util.StaxParserUtil; -import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLEntityDescriptorParser; +import org.keycloak.saml.processing.core.parsers.saml.SAMLParser; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.util.AdminEventPaths; import org.w3c.dom.NodeList; @@ -599,8 +598,8 @@ public class IdentityProviderTest extends AbstractAdminTest { private void assertSamlExport(String body) throws ParsingException, URISyntaxException { //System.out.println(body); - Object entBody = new SAMLEntityDescriptorParser().parse(StaxParserUtil.getXMLEventReader( - new ByteArrayInputStream(body.getBytes(Charset.forName("utf-8"))))); + Object entBody = SAMLParser.getInstance().parse( + new ByteArrayInputStream(body.getBytes(Charset.forName("utf-8")))); Assert.assertEquals("Parsed export type", EntityDescriptorType.class, entBody.getClass()); EntityDescriptorType entity = (EntityDescriptorType) entBody; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java index 484b9cc698..e84daa1551 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java @@ -26,7 +26,6 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.testsuite.util.SamlClientBuilder; -import java.net.URI; import java.util.List; import org.hamcrest.Matcher; import org.junit.Test; @@ -68,21 +67,21 @@ public class AuthnRequestNameIdFormatTest extends AbstractSamlTest { @Test public void testPostLoginNameIdPolicyUnspecified() throws Exception { NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); - nameIdPolicy.setFormat(URI.create(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())); + nameIdPolicy.setFormat(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.getUri()); testLoginWithNameIdPolicy(Binding.POST, Binding.POST, nameIdPolicy, is("bburke")); } @Test public void testPostLoginNameIdPolicyEmail() throws Exception { NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); - nameIdPolicy.setFormat(URI.create(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())); + nameIdPolicy.setFormat(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.getUri()); testLoginWithNameIdPolicy(Binding.POST, Binding.POST, nameIdPolicy, is("bburke@redhat.com")); } @Test public void testPostLoginNameIdPolicyPersistent() throws Exception { NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); - nameIdPolicy.setFormat(URI.create(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get())); + nameIdPolicy.setFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.getUri()); testLoginWithNameIdPolicy(Binding.POST, Binding.POST, nameIdPolicy, startsWith("G-")); } @@ -94,21 +93,21 @@ public class AuthnRequestNameIdFormatTest extends AbstractSamlTest { @Test public void testRedirectLoginNameIdPolicyUnspecified() throws Exception { NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); - nameIdPolicy.setFormat(URI.create(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get())); + nameIdPolicy.setFormat(JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.getUri()); testLoginWithNameIdPolicy(Binding.REDIRECT, Binding.REDIRECT, nameIdPolicy, is("bburke")); } @Test public void testRedirectLoginNameIdPolicyEmail() throws Exception { NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); - nameIdPolicy.setFormat(URI.create(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())); + nameIdPolicy.setFormat(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.getUri()); testLoginWithNameIdPolicy(Binding.REDIRECT, Binding.REDIRECT, nameIdPolicy, is("bburke@redhat.com")); } @Test public void testRedirectLoginNameIdPolicyPersistent() throws Exception { NameIDPolicyType nameIdPolicy = new NameIDPolicyType(); - nameIdPolicy.setFormat(URI.create(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get())); + nameIdPolicy.setFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.getUri()); testLoginWithNameIdPolicy(Binding.REDIRECT, Binding.REDIRECT, nameIdPolicy, startsWith("G-")); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/LogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/LogoutTest.java index fb64e9ac83..24967aee5f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/LogoutTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/LogoutTest.java @@ -276,7 +276,7 @@ public class LogoutTest extends AbstractSamlTest { .processSamlResponse(REDIRECT) .transformDocument(doc -> { // Expect logout request for sales-post2 - SAML2Object so = (SAML2Object) new SAMLParser().parse(new DOMSource(doc)); + SAML2Object so = (SAML2Object) SAMLParser.getInstance().parse(new DOMSource(doc)); assertThat(so, isSamlLogoutRequest("http://url-to-sales-2")); // Emulate successful logout response from sales-post2 logout diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/saml/SamlEcpProfileTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/saml/SamlEcpProfileTest.java index bc6d316a72..5ac5722392 100755 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/saml/SamlEcpProfileTest.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/saml/SamlEcpProfileTest.java @@ -138,7 +138,7 @@ public class SamlEcpProfileTest { SOAPHeader responseMessageHeaders = responseMessage.getSOAPHeader(); - NodeList ecpResponse = responseMessageHeaders.getElementsByTagNameNS(JBossSAMLURIConstants.ECP_PROFILE.get(), JBossSAMLConstants.RESPONSE.get()); + NodeList ecpResponse = responseMessageHeaders.getElementsByTagNameNS(JBossSAMLURIConstants.ECP_PROFILE.get(), JBossSAMLConstants.RESPONSE__ECP.get()); assertEquals("No ECP Response", 1, ecpResponse.getLength()); @@ -146,7 +146,7 @@ public class SamlEcpProfileTest { assertNotNull(samlResponse); - ResponseType responseType = (ResponseType) new SAMLParser().parse(samlResponse); + ResponseType responseType = (ResponseType) SAMLParser.getInstance().parse(samlResponse); StatusCodeType statusCode = responseType.getStatus().getStatusCode(); assertEquals(statusCode.getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get()); @@ -229,7 +229,7 @@ public class SamlEcpProfileTest { assertNotNull(samlResponse); - StatusResponseType responseType = (StatusResponseType) new SAMLParser().parse(samlResponse); + StatusResponseType responseType = (StatusResponseType) SAMLParser.getInstance().parse(samlResponse); StatusCodeType statusCode = responseType.getStatus().getStatusCode(); assertNotEquals(statusCode.getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());