diff --git a/adapters/saml/core/pom.xml b/adapters/saml/core/pom.xml index 5a63c2c44a..8defcfa9eb 100755 --- a/adapters/saml/core/pom.xml +++ b/adapters/saml/core/pom.xml @@ -71,6 +71,11 @@ junit test + + org.hamcrest + hamcrest-all + test + org.apache.httpcomponents httpclient diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java index 76d7648425..a32e122132 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java @@ -27,35 +27,36 @@ import org.keycloak.adapters.cloned.AdapterHttpClientConfig; */ public class IDP implements Serializable { public static class SingleSignOnService implements Serializable { - private boolean signRequest; - private boolean validateResponseSignature; + private Boolean signRequest; + private Boolean validateResponseSignature; private String requestBinding; private String responseBinding; private String bindingUrl; private String assertionConsumerServiceUrl; - private boolean validateAssertionSignature; + private Boolean validateAssertionSignature; + private boolean signaturesRequired = false; public boolean isSignRequest() { - return signRequest; + return signRequest == null ? signaturesRequired : signRequest; } - public void setSignRequest(boolean signRequest) { + public void setSignRequest(Boolean signRequest) { this.signRequest = signRequest; } public boolean isValidateResponseSignature() { - return validateResponseSignature; + return validateResponseSignature == null ? signaturesRequired : validateResponseSignature; } - public void setValidateResponseSignature(boolean validateResponseSignature) { + public void setValidateResponseSignature(Boolean validateResponseSignature) { this.validateResponseSignature = validateResponseSignature; } public boolean isValidateAssertionSignature() { - return validateAssertionSignature; + return validateAssertionSignature == null ? false : validateAssertionSignature; } - public void setValidateAssertionSignature(boolean validateAssertionSignature) { + public void setValidateAssertionSignature(Boolean validateAssertionSignature) { this.validateAssertionSignature = validateAssertionSignature; } @@ -90,47 +91,52 @@ public class IDP implements Serializable { public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) { this.assertionConsumerServiceUrl = assertionConsumerServiceUrl; } + + private void setSignaturesRequired(boolean signaturesRequired) { + this.signaturesRequired = signaturesRequired; + } } public static class SingleLogoutService implements Serializable { - private boolean signRequest; - private boolean signResponse; - private boolean validateRequestSignature; - private boolean validateResponseSignature; + private Boolean signRequest; + private Boolean signResponse; + private Boolean validateRequestSignature; + private Boolean validateResponseSignature; private String requestBinding; private String responseBinding; private String postBindingUrl; private String redirectBindingUrl; + private boolean signaturesRequired = false; public boolean isSignRequest() { - return signRequest; + return signRequest == null ? signaturesRequired : signRequest; } - public void setSignRequest(boolean signRequest) { + public void setSignRequest(Boolean signRequest) { this.signRequest = signRequest; } public boolean isSignResponse() { - return signResponse; + return signResponse == null ? signaturesRequired : signResponse; } - public void setSignResponse(boolean signResponse) { + public void setSignResponse(Boolean signResponse) { this.signResponse = signResponse; } public boolean isValidateRequestSignature() { - return validateRequestSignature; + return validateRequestSignature == null ? signaturesRequired : validateRequestSignature; } - public void setValidateRequestSignature(boolean validateRequestSignature) { + public void setValidateRequestSignature(Boolean validateRequestSignature) { this.validateRequestSignature = validateRequestSignature; } public boolean isValidateResponseSignature() { - return validateResponseSignature; + return validateResponseSignature == null ? signaturesRequired : validateResponseSignature; } - public void setValidateResponseSignature(boolean validateResponseSignature) { + public void setValidateResponseSignature(Boolean validateResponseSignature) { this.validateResponseSignature = validateResponseSignature; } @@ -165,6 +171,10 @@ public class IDP implements Serializable { public void setRedirectBindingUrl(String redirectBindingUrl) { this.redirectBindingUrl = redirectBindingUrl; } + + private void setSignaturesRequired(boolean signaturesRequired) { + this.signaturesRequired = signaturesRequired; + } } public static class HttpClientConfig implements AdapterHttpClientConfig { @@ -258,6 +268,7 @@ public class IDP implements Serializable { private SingleLogoutService singleLogoutService; private List keys; private AdapterHttpClientConfig httpClientConfig = new HttpClientConfig(); + private boolean signaturesRequired = false; public String getEntityID() { return entityID; @@ -273,6 +284,9 @@ public class IDP implements Serializable { public void setSingleSignOnService(SingleSignOnService singleSignOnService) { this.singleSignOnService = singleSignOnService; + if (singleSignOnService != null) { + singleSignOnService.setSignaturesRequired(signaturesRequired); + } } public SingleLogoutService getSingleLogoutService() { @@ -281,6 +295,9 @@ public class IDP implements Serializable { public void setSingleLogoutService(SingleLogoutService singleLogoutService) { this.singleLogoutService = singleLogoutService; + if (singleLogoutService != null) { + singleLogoutService.setSignaturesRequired(signaturesRequired); + } } public List getKeys() { @@ -315,4 +332,12 @@ public class IDP implements Serializable { this.httpClientConfig = httpClientConfig; } + public boolean isSignaturesRequired() { + return signaturesRequired; + } + + public void setSignaturesRequired(boolean signaturesRequired) { + this.signaturesRequired = signaturesRequired; + } + } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/Key.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/Key.java index ebf3b37bf6..5c64ae3778 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/Key.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/Key.java @@ -114,16 +114,16 @@ public class Key implements Serializable { return signing; } - public void setSigning(boolean signing) { - this.signing = signing; + public void setSigning(Boolean signing) { + this.signing = signing != null && signing; } public boolean isEncryption() { return encryption; } - public void setEncryption(boolean encryption) { - this.encryption = encryption; + public void setEncryption(Boolean encryption) { + this.encryption = encryption != null && encryption; } public KeyStoreConfig getKeystore() { diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java index a103ffc34b..0cc474a383 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/KeycloakSamlAdapter.java @@ -26,13 +26,14 @@ import java.util.List; * @version $Revision: 1 $ */ public class KeycloakSamlAdapter implements Serializable { - private List sps = new LinkedList<>(); + private final List sps = new LinkedList<>(); public List getSps() { return sps; } - public void setSps(List sps) { - this.sps = sps; + public void addSp(SP sp) { + sps.add(sp); } + } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/SP.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/SP.java index 071b170d2f..9b1ba35417 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/SP.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/SP.java @@ -80,24 +80,24 @@ public class SP implements Serializable { return forceAuthentication; } - public void setForceAuthentication(boolean forceAuthentication) { - this.forceAuthentication = forceAuthentication; + public void setForceAuthentication(Boolean forceAuthentication) { + this.forceAuthentication = forceAuthentication != null && forceAuthentication; } public boolean isIsPassive() { return isPassive; } - public void setIsPassive(boolean isPassive) { - this.isPassive = isPassive; + public void setIsPassive(Boolean isPassive) { + this.isPassive = isPassive != null && isPassive; } public boolean isTurnOffChangeSessionIdOnLogin() { return turnOffChangeSessionIdOnLogin; } - public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) { - this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin; + public void setTurnOffChangeSessionIdOnLogin(Boolean turnOffChangeSessionIdOnLogin) { + this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin != null && turnOffChangeSessionIdOnLogin; } public List getKeys() { @@ -152,7 +152,7 @@ public class SP implements Serializable { return autodetectBearerOnly; } - public void setAutodetectBearerOnly(boolean autodetectBearerOnly) { - this.autodetectBearerOnly = autodetectBearerOnly; + public void setAutodetectBearerOnly(Boolean autodetectBearerOnly) { + this.autodetectBearerOnly = autodetectBearerOnly != null && autodetectBearerOnly; } } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/AbstractKeycloakSamlAdapterV1Parser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/AbstractKeycloakSamlAdapterV1Parser.java new file mode 100644 index 0000000000..361c03e6c1 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/AbstractKeycloakSamlAdapterV1Parser.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.adapters.saml.config.parsers; + +import org.keycloak.saml.common.parsers.AbstractStaxParser; + +import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.saml.processing.core.parsers.util.QNameEnumLookup; +import java.util.Collections; +import java.util.Set; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.stream.events.StartElement; + +/** + * + */ +public abstract class AbstractKeycloakSamlAdapterV1Parser extends AbstractStaxParser { + + protected static final QNameEnumLookup LOOKUP = new QNameEnumLookup(KeycloakSamlAdapterV1QNames.values()); + + private static final Set ALTERNATE_NAMESPACES = Collections.singleton(XMLConstants.NULL_NS_URI); + + public AbstractKeycloakSamlAdapterV1Parser(KeycloakSamlAdapterV1QNames expectedStartElement) { + super(expectedStartElement.getQName(), KeycloakSamlAdapterV1QNames.UNKNOWN_ELEMENT); + } + + @Override + protected KeycloakSamlAdapterV1QNames getElementFromName(QName name) { + return (ALTERNATE_NAMESPACES.contains(name.getNamespaceURI())) + ? LOOKUP.from(new QName(KeycloakSamlAdapterV1QNames.NS_URI, name.getLocalPart())) + : LOOKUP.from(name); + } + + @Override + protected void validateStartElement(StartElement startElement) { + QName name = startElement.getName(); + QName validatedQName = ALTERNATE_NAMESPACES.contains(name.getNamespaceURI()) + ? new QName(name.getNamespaceURI(), expectedStartElement.getLocalPart()) + : expectedStartElement; + StaxParserUtil.validate(startElement, validatedQName); + } + +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java deleted file mode 100755 index ecde04a3fc..0000000000 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java +++ /dev/null @@ -1,88 +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.adapters.saml.config.parsers; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class ConfigXmlConstants { - public static final String KEYCLOAK_SAML_ADAPTER = "keycloak-saml-adapter"; - public static final String SP_ELEMENT = "SP"; - public static final String ENTITY_ID_ATTR = "entityID"; - public static final String SSL_POLICY_ATTR = "sslPolicy"; - public static final String NAME_ID_POLICY_FORMAT_ATTR = "nameIDPolicyFormat"; - public static final String FORCE_AUTHENTICATION_ATTR = "forceAuthentication"; - public static final String IS_PASSIVE_ATTR = "isPassive"; - public static final String TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN_ATTR = "turnOffChangeSessionIdOnLogin"; - public static final String SIGNATURE_ALGORITHM_ATTR = "signatureAlgorithm"; - public static final String SIGNATURE_CANONICALIZATION_METHOD_ATTR = "signatureCanonicalizationMethod"; - public static final String LOGOUT_PAGE_ATTR = "logoutPage"; - public static final String AUTODETECT_BEARER_ONLY_ATTR = "autodetectBearerOnly"; - - public static final String KEYS_ELEMENT = "Keys"; - public static final String KEY_ELEMENT = "Key"; - public static final String SIGNING_ATTR = "signing"; - public static final String ENCRYPTION_ATTR = "encryption"; - public static final String CERTIFICATE_PEM_ELEMENT = "CertificatePem"; - public static final String PRIVATE_KEY_PEM_ELEMENT = "PrivateKeyPem"; - public static final String PUBLIC_KEY_PEM_ELEMENT = "PublicKeyPem"; - public static final String FILE_ATTR = "file"; - public static final String TYPE_ATTR = "type"; - public static final String RESOURCE_ATTR = "resource"; - public static final String PASSWORD_ATTR = "password"; - public static final String ALIAS_ATTR = "alias"; - public static final String KEYS_STORE_ELEMENT = "KeyStore"; - public static final String CERTIFICATE_ELEMENT = "Certificate"; - public static final String PRIVATE_KEY_ELEMENT = "PrivateKey"; - - public static final String PRINCIPAL_NAME_MAPPING_ELEMENT = "PrincipalNameMapping"; - public static final String POLICY_ATTR = "policy"; - public static final String ATTRIBUTE_ATTR = "attribute"; - - public static final String ROLE_IDENTIFIERS_ELEMENT = "RoleIdentifiers"; - public static final String ATTRIBUTE_ELEMENT = "Attribute"; - public static final String NAME_ATTR = "name"; - - public static final String IDP_ELEMENT = "IDP"; - public static final String SIGNATURES_REQUIRED_ATTR = "signaturesRequired"; - public static final String SINGLE_SIGN_ON_SERVICE_ELEMENT = "SingleSignOnService"; - public static final String SINGLE_LOGOUT_SERVICE_ELEMENT = "SingleLogoutService"; - public static final String SIGN_REQUEST_ATTR = "signRequest"; - public static final String SIGN_RESPONSE_ATTR = "signResponse"; - public static final String REQUEST_BINDING_ATTR = "requestBinding"; - public static final String RESPONSE_BINDING_ATTR = "responseBinding"; - public static final String BINDING_URL_ATTR = "bindingUrl"; - public static final String VALIDATE_RESPONSE_SIGNATURE_ATTR = "validateResponseSignature"; - public static final String VALIDATE_ASSERTION_SIGNATURE_ATTR = "validateAssertionSignature"; - public static final String VALIDATE_REQUEST_SIGNATURE_ATTR = "validateRequestSignature"; - public static final String POST_BINDING_URL_ATTR = "postBindingUrl"; - public static final String REDIRECT_BINDING_URL_ATTR = "redirectBindingUrl"; - public static final String ASSERTION_CONSUMER_SERVICE_URL_ATTR = "assertionConsumerServiceUrl"; - - public static final String HTTP_CLIENT_ELEMENT = "HttpClient"; - public static final String ALLOW_ANY_HOSTNAME_ATTR = "allowAnyHostname"; - public static final String CLIENT_KEYSTORE_ATTR = "clientKeystore"; - public static final String CLIENT_KEYSTORE_PASSWORD_ATTR = "clientKeystorePassword"; - public static final String CONNECTION_POOL_SIZE_ATTR = "connectionPoolSize"; - public static final String DISABLE_TRUST_MANAGER_ATTR = "disableTrustManager"; - public static final String PROXY_URL_ATTR = "proxyUrl"; - public static final String TRUSTSTORE_ATTR = "truststore"; - public static final String TRUSTSTORE_PASSWORD_ATTR = "truststorePassword"; - -} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java index 92254a2483..f06a22790d 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java @@ -59,7 +59,7 @@ public class DeploymentBuilder { idp.setSingleSignOnService(sso); idp.setSingleLogoutService(slo); - KeycloakSamlAdapter adapter = (KeycloakSamlAdapter)(new KeycloakSamlAdapterXMLParser().parse(xml)); + KeycloakSamlAdapter adapter = (KeycloakSamlAdapter) KeycloakSamlAdapterParser.getInstance().parse(xml); SP sp = adapter.getSps().get(0); deployment.setConfigured(true); deployment.setEntityID(sp.getEntityID()); diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/HttpClientParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/HttpClientParser.java new file mode 100644 index 0000000000..b365fd7494 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/HttpClientParser.java @@ -0,0 +1,65 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.IDP.HttpClientConfig; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class HttpClientParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final HttpClientParser INSTANCE = new HttpClientParser(); + + private HttpClientParser() { + super(KeycloakSamlAdapterV1QNames.HTTP_CLIENT); + } + + public static HttpClientParser getInstance() { + return INSTANCE; + } + + @Override + protected HttpClientConfig instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final HttpClientConfig config = new HttpClientConfig(); + + final Boolean allowAnyHostname = StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ALLOW_ANY_HOSTNAME); + config.setAllowAnyHostname(allowAnyHostname == null ? false : allowAnyHostname); + config.setClientKeystore(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_CLIENT_KEYSTORE)); + config.setClientKeystorePassword(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_CLIENT_KEYSTORE_PASSWORD)); + final Integer connPoolSize = StaxParserUtil.getIntegerAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_CONNECTION_POOL_SIZE); + config.setConnectionPoolSize(connPoolSize == null ? 0 : connPoolSize); + final Boolean disableTrustManager = StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_DISABLE_TRUST_MANAGER); + config.setDisableTrustManager(disableTrustManager == null ? false : disableTrustManager); + config.setProxyUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_PROXY_URL)); + config.setTruststore(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TRUSTSTORE)); + config.setTruststorePassword(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TRUSTSTORE_PASSWORD)); + + return config; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, HttpClientConfig target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + } +} 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 deleted file mode 100755 index 6dca7aadf3..0000000000 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java +++ /dev/null @@ -1,159 +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.adapters.saml.config.parsers; - -import org.keycloak.adapters.saml.config.IDP; -import org.keycloak.adapters.saml.config.Key; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; -import org.keycloak.saml.common.util.StaxParserUtil; - -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 java.util.List; -import org.keycloak.adapters.saml.config.IDP.HttpClientConfig; -import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getAttributeValue; -import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getBooleanAttributeValue; -import static org.keycloak.adapters.saml.config.parsers.SPXmlParser.getIntegerAttributeValue; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class IDPXmlParser extends AbstractParser { - - @Override - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT); - IDP idp = new IDP(); - String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR); - if (entityID == null) { - throw new ParsingException("entityID must be set on IDP"); - - } - idp.setEntityID(entityID); - - boolean signaturesRequired = getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR); - idp.setSignatureCanonicalizationMethod(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR)); - idp.setSignatureAlgorithm(getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR)); - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getElementName(endElement); - if (endElementName.equals(ConfigXmlConstants.IDP_ELEMENT)) - break; - else - continue; - } - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getElementName(startElement); - if (tag.equals(ConfigXmlConstants.SINGLE_SIGN_ON_SERVICE_ELEMENT)) { - IDP.SingleSignOnService sso = parseSingleSignOnService(xmlEventReader, signaturesRequired); - idp.setSingleSignOnService(sso); - - } else if (tag.equals(ConfigXmlConstants.SINGLE_LOGOUT_SERVICE_ELEMENT)) { - IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader, signaturesRequired); - idp.setSingleLogoutService(slo); - - } else if (tag.equals(ConfigXmlConstants.HTTP_CLIENT_ELEMENT)) { - HttpClientConfig config = parseHttpClientElement(xmlEventReader); - idp.setHttpClientConfig(config); - - } else if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) { - KeysXmlParser parser = new KeysXmlParser(); - List keys = (List)parser.parse(xmlEventReader); - idp.setKeys(keys); - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } - - } - return idp; - } - - protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException { - IDP.SingleLogoutService slo = new IDP.SingleLogoutService(); - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - slo.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired)); - slo.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired)); - slo.setValidateRequestSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired)); - slo.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR)); - slo.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR)); - slo.setSignResponse(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired)); - slo.setPostBindingUrl(getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR)); - slo.setRedirectBindingUrl(getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR)); - return slo; - } - - protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException { - IDP.SingleSignOnService sso = new IDP.SingleSignOnService(); - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - sso.setSignRequest(getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired)); - sso.setValidateResponseSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired)); - sso.setValidateAssertionSignature(getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_ASSERTION_SIGNATURE_ATTR)); - sso.setRequestBinding(getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR)); - sso.setResponseBinding(getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR)); - sso.setBindingUrl(getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR)); - sso.setAssertionConsumerServiceUrl(getAttributeValue(element, ConfigXmlConstants.ASSERTION_CONSUMER_SERVICE_URL_ATTR)); - return sso; - } - - private HttpClientConfig parseHttpClientElement(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.HTTP_CLIENT_ELEMENT); - HttpClientConfig config = new HttpClientConfig(); - - config.setAllowAnyHostname(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false)); - config.setClientKeystore(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_ATTR)); - config.setClientKeystorePassword(getAttributeValue(startElement, ConfigXmlConstants.CLIENT_KEYSTORE_PASSWORD_ATTR)); - config.setConnectionPoolSize(getIntegerAttributeValue(startElement, ConfigXmlConstants.CONNECTION_POOL_SIZE_ATTR, 0)); - config.setDisableTrustManager(getBooleanAttributeValue(startElement, ConfigXmlConstants.ALLOW_ANY_HOSTNAME_ATTR, false)); - config.setProxyUrl(getAttributeValue(startElement, ConfigXmlConstants.PROXY_URL_ATTR)); - config.setTruststore(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_ATTR)); - config.setTruststorePassword(getAttributeValue(startElement, ConfigXmlConstants.TRUSTSTORE_PASSWORD_ATTR)); - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getElementName(endElement); - if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) - break; - else - continue; - } - - String tag = StaxParserUtil.getElementName(startElement); - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } - - return config; - } - -} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IdpParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IdpParser.java new file mode 100644 index 0000000000..f883658888 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IdpParser.java @@ -0,0 +1,77 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.IDP; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class IdpParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final IdpParser INSTANCE = new IdpParser(); + + private IdpParser() { + super(KeycloakSamlAdapterV1QNames.IDP); + } + + public static IdpParser getInstance() { + return INSTANCE; + } + + @Override + protected IDP instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final IDP idp = new IDP(); + + idp.setEntityID(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ENTITY_ID)); + + Boolean signaturesRequired = StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNATURES_REQUIRED); + idp.setSignaturesRequired(signaturesRequired == null ? false : signaturesRequired); + idp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNATURE_CANONICALIZATION_METHOD)); + idp.setSignatureAlgorithm(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNATURE_ALGORITHM)); + + return idp; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, IDP target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case HTTP_CLIENT: + target.setHttpClientConfig(HttpClientParser.getInstance().parse(xmlEventReader)); + break; + + case KEYS: + target.setKeys(KeysParser.getInstance().parse(xmlEventReader)); + break; + + case SINGLE_SIGN_ON_SERVICE: + target.setSingleSignOnService(SingleSignOnServiceParser.getInstance().parse(xmlEventReader)); + break; + + case SINGLE_LOGOUT_SERVICE: + target.setSingleLogoutService(SingleLogoutServiceParser.getInstance().parse(xmlEventReader)); + break; + } + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyParser.java new file mode 100644 index 0000000000..d6b59f5a12 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyParser.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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.Key; +import org.keycloak.common.util.StringPropertyReplacer; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class KeyParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final KeyParser INSTANCE = new KeyParser(); + + private KeyParser() { + super(KeycloakSamlAdapterV1QNames.KEY); + } + + public static KeyParser getInstance() { + return INSTANCE; + } + + @Override + protected Key instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + Key key = new Key(); + key.setSigning(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGNING)); + key.setEncryption(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ENCRYPTION)); + return key; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, Key target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + String value; + switch (element) { + case KEY_STORE: + target.setKeystore(KeyStoreParser.getInstance().parse(xmlEventReader)); + break; + + case CERTIFICATE_PEM: + StaxParserUtil.advance(xmlEventReader); + value = StaxParserUtil.getElementText(xmlEventReader); + target.setCertificatePem(StringPropertyReplacer.replaceProperties(value)); + break; + + case PUBLIC_KEY_PEM: + StaxParserUtil.advance(xmlEventReader); + value = StaxParserUtil.getElementText(xmlEventReader); + target.setPublicKeyPem(StringPropertyReplacer.replaceProperties(value)); + break; + + case PRIVATE_KEY_PEM: + StaxParserUtil.advance(xmlEventReader); + value = StaxParserUtil.getElementText(xmlEventReader); + target.setPrivateKeyPem(StringPropertyReplacer.replaceProperties(value)); + break; + } + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyStoreParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyStoreParser.java new file mode 100644 index 0000000000..9cdfaf4e1f --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyStoreParser.java @@ -0,0 +1,73 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.Key; +import org.keycloak.adapters.saml.config.Key.KeyStoreConfig; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class KeyStoreParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final KeyStoreParser INSTANCE = new KeyStoreParser(); + + private KeyStoreParser() { + super(KeycloakSamlAdapterV1QNames.KEY_STORE); + } + + public static KeyStoreParser getInstance() { + return INSTANCE; + } + + @Override + protected KeyStoreConfig instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final KeyStoreConfig keyStore = new Key.KeyStoreConfig(); + keyStore.setType(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TYPE)); + keyStore.setAlias(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ALIAS)); + keyStore.setFile(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_FILE)); + keyStore.setResource(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_RESOURCE)); + keyStore.setPassword(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_PASSWORD)); + + if (keyStore.getFile() == null && keyStore.getResource() == null) { + throw new ParsingException("KeyStore element must have the url or classpath attribute set"); + } + + return keyStore; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, KeyStoreConfig target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case CERTIFICATE: + target.setCertificateAlias(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_ALIAS)); + break; + + case PRIVATE_KEY: + target.setPrivateKeyAlias(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_ALIAS)); + target.setPrivateKeyPassword(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_PASSWORD)); + break; + } + } +} 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 deleted file mode 100755 index 258b21adfa..0000000000 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java +++ /dev/null @@ -1,141 +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.adapters.saml.config.parsers; - -import org.keycloak.adapters.saml.config.Key; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; -import org.keycloak.saml.common.util.StaxParserUtil; - -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; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class KeyXmlParser extends AbstractParser { - - @Override - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT); - Key key = new Key(); - key.setSigning(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR)); - key.setEncryption(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR)); - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getElementName(endElement); - if (endElementName.equals(ConfigXmlConstants.KEY_ELEMENT)) - break; - else - throw logger.parserUnknownEndElement(endElementName, xmlEvent.getLocation()); - } - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getElementName(startElement); - if (tag.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) { - key.setKeystore(parseKeyStore(xmlEventReader)); - } else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) { - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - key.setCertificatePem(SPXmlParser.getElementText(xmlEventReader)); - } else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) { - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - key.setPublicKeyPem(SPXmlParser.getElementText(xmlEventReader)); - } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) { - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - key.setPrivateKeyPem(SPXmlParser.getElementText(xmlEventReader)); - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } - - } - return key; - } - - protected Key.KeyStoreConfig parseKeyStore(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT); - Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig(); - keyStore.setType(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR)); - keyStore.setAlias(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR)); - keyStore.setFile(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR)); - keyStore.setResource(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR)); - if (keyStore.getFile() == null && keyStore.getResource() == null) { - throw new ParsingException("KeyStore element must have the url or classpath attribute set"); - } - keyStore.setPassword(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR)); - if (keyStore.getPassword() == null) { - throw new ParsingException("KeyStore element must have the password attribute set"); - } - - - - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getElementName(endElement); - if (endElementName.equals(ConfigXmlConstants.KEYS_STORE_ELEMENT)) - break; - else - continue; - } - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getElementName(startElement); - if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) { - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - keyStore.setCertificateAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR)); - if (keyStore.getCertificateAlias() == null) { - throw new ParsingException("KeyStore Certificate element must have the alias attribute set"); - - } - } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) { - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - keyStore.setPrivateKeyAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR)); - if (keyStore.getPrivateKeyAlias() == null) { - throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set"); - - } - keyStore.setPrivateKeyPassword(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR)); - if (keyStore.getPrivateKeyPassword() == null) { - throw new ParsingException("KeyStore PrivateKey element must have the password attribute set"); - - } - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } - - } - return keyStore; - - } - -} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterParser.java new file mode 100644 index 0000000000..2b3bf60002 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterParser.java @@ -0,0 +1,85 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.saml.common.ErrorCodes; +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 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; + +/** + * + * @author hmlnarik + */ +public class KeycloakSamlAdapterParser extends AbstractParser { + + private interface ParserFactory { + public StaxParser create(); + } + private static final Map PARSERS = new HashMap(); + + // No-namespace variant + private static final QName ALTERNATE_KEYCLOAK_SAML_ADAPTER_V1 = new QName(KeycloakSamlAdapterV1QNames.KEYCLOAK_SAML_ADAPTER.getQName().getLocalPart()); + + static { + PARSERS.put(KeycloakSamlAdapterV1QNames.KEYCLOAK_SAML_ADAPTER.getQName(), new ParserFactory() { @Override public StaxParser create() { return KeycloakSamlAdapterV1Parser.getInstance(); }}); + PARSERS.put(ALTERNATE_KEYCLOAK_SAML_ADAPTER_V1, new ParserFactory() { @Override public StaxParser create() { return KeycloakSamlAdapterV1Parser.getInstance(); }}); + } + + private static final KeycloakSamlAdapterParser INSTANCE = new KeycloakSamlAdapterParser(); + + public static KeycloakSamlAdapterParser getInstance() { + return INSTANCE; + } + + protected KeycloakSamlAdapterParser() { + } + + /** + * @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; + final QName name = startElement.getName(); + + ParserFactory pf = PARSERS.get(name); + if (pf == null) { + throw logger.parserException(new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + name + "::location=" + + startElement.getLocation())); + } + + return pf.create().parse(xmlEventReader); + } + + StaxParserUtil.getNextEvent(xmlEventReader); + } + + throw new RuntimeException(ErrorCodes.FAILED_PARSING + "SAML Parsing has failed"); + } +} 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/KeycloakSamlAdapterV1Parser.java similarity index 53% rename from adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParser.java rename to adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterV1Parser.java index b9739388c5..72d35f9905 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/KeycloakSamlAdapterV1Parser.java @@ -18,12 +18,9 @@ package org.keycloak.adapters.saml.config.parsers; import org.keycloak.adapters.saml.config.KeycloakSamlAdapter; -import org.keycloak.adapters.saml.config.SP; import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; import org.keycloak.saml.common.util.StaxParserUtil; -import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.events.StartElement; @@ -31,28 +28,33 @@ import javax.xml.stream.events.StartElement; * @author Bill Burke * @version $Revision: 1 $ */ -public class KeycloakSamlAdapterXMLParser extends AbstractParser { +public class KeycloakSamlAdapterV1Parser extends AbstractKeycloakSamlAdapterV1Parser { - @Override - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - KeycloakSamlAdapter adapter = new KeycloakSamlAdapter(); - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYCLOAK_SAML_ADAPTER); - while (xmlEventReader.hasNext()) { - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getElementName(startElement); - if (tag.equals(ConfigXmlConstants.SP_ELEMENT)) { - SPXmlParser parser = new SPXmlParser(); - SP sp = (SP)parser.parse(xmlEventReader); - if (sp != null) adapter.getSps().add(sp); - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } + private static final KeycloakSamlAdapterV1Parser INSTANCE = new KeycloakSamlAdapterV1Parser(); - } - return adapter; + private KeycloakSamlAdapterV1Parser() { + super(KeycloakSamlAdapterV1QNames.KEYCLOAK_SAML_ADAPTER); } + public static KeycloakSamlAdapterV1Parser getInstance() { + return INSTANCE; + } + + @Override + protected KeycloakSamlAdapter instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new KeycloakSamlAdapter(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, KeycloakSamlAdapter target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case SP: + target.addSp(SpParser.getInstance().parse(xmlEventReader)); + break; + + default: + // Ignore unknown tags + StaxParserUtil.bypassElementBlock(xmlEventReader); + } + } } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterV1QNames.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterV1QNames.java new file mode 100644 index 0000000000..73e4a71a35 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterV1QNames.java @@ -0,0 +1,114 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.saml.processing.core.parsers.util.HasQName; +import javax.xml.namespace.QName; + +/** + * + * @author hmlnarik + */ +public enum KeycloakSamlAdapterV1QNames implements HasQName { + + ATTRIBUTE("Attribute"), + CERTIFICATE("Certificate"), + CERTIFICATE_PEM("CertificatePem"), + HTTP_CLIENT("HttpClient"), + IDP("IDP"), + KEY("Key"), + KEYCLOAK_SAML_ADAPTER("keycloak-saml-adapter"), + KEYS("Keys"), + KEY_STORE("KeyStore"), + PRINCIPAL_NAME_MAPPING("PrincipalNameMapping"), + PRIVATE_KEY("PrivateKey"), + PRIVATE_KEY_PEM("PrivateKeyPem"), + PUBLIC_KEY_PEM("PublicKeyPem"), + ROLE_IDENTIFIERS("RoleIdentifiers"), + SINGLE_LOGOUT_SERVICE("SingleLogoutService"), + SINGLE_SIGN_ON_SERVICE("SingleSignOnService"), + SP("SP"), + + ATTR_ALIAS(null, "alias"), + ATTR_ALLOW_ANY_HOSTNAME(null, "allowAnyHostname"), + ATTR_ASSERTION_CONSUMER_SERVICE_URL(null, "assertionConsumerServiceUrl"), + ATTR_ATTRIBUTE(null, "attribute"), + ATTR_AUTODETECT_BEARER_ONLY(null, "autodetectBearerOnly"), + ATTR_BINDING_URL(null, "bindingUrl"), + ATTR_CLIENT_KEYSTORE(null, "clientKeystore"), + ATTR_CLIENT_KEYSTORE_PASSWORD(null, "clientKeystorePassword"), + ATTR_CONNECTION_POOL_SIZE(null, "connectionPoolSize"), + ATTR_DISABLE_TRUST_MANAGER(null, "disableTrustManager"), + ATTR_ENCRYPTION(null, "encryption"), + ATTR_ENTITY_ID(null, "entityID"), + ATTR_FILE(null, "file"), + ATTR_FORCE_AUTHENTICATION(null, "forceAuthentication"), + ATTR_IS_PASSIVE(null, "isPassive"), + ATTR_LOGOUT_PAGE(null, "logoutPage"), + ATTR_NAME(null, "name"), + ATTR_NAME_ID_POLICY_FORMAT(null, "nameIDPolicyFormat"), + ATTR_PASSWORD(null, "password"), + ATTR_POLICY(null, "policy"), + ATTR_POST_BINDING_URL(null, "postBindingUrl"), + ATTR_PROXY_URL(null, "proxyUrl"), + ATTR_REDIRECT_BINDING_URL(null, "redirectBindingUrl"), + ATTR_REQUEST_BINDING(null, "requestBinding"), + ATTR_RESOURCE(null, "resource"), + ATTR_RESPONSE_BINDING(null, "responseBinding"), + ATTR_SIGNATURES_REQUIRED(null, "signaturesRequired"), + ATTR_SIGNATURE_ALGORITHM(null, "signatureAlgorithm"), + ATTR_SIGNATURE_CANONICALIZATION_METHOD(null, "signatureCanonicalizationMethod"), + ATTR_SIGNING(null, "signing"), + ATTR_SIGN_REQUEST(null, "signRequest"), + ATTR_SIGN_RESPONSE(null, "signResponse"), + ATTR_SSL_POLICY(null, "sslPolicy"), + ATTR_TRUSTSTORE(null, "truststore"), + ATTR_TRUSTSTORE_PASSWORD(null, "truststorePassword"), + ATTR_TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN(null, "turnOffChangeSessionIdOnLogin"), + ATTR_TYPE(null, "type"), + ATTR_VALIDATE_ASSERTION_SIGNATURE(null, "validateAssertionSignature"), + ATTR_VALIDATE_REQUEST_SIGNATURE(null, "validateRequestSignature"), + ATTR_VALIDATE_RESPONSE_SIGNATURE(null, "validateResponseSignature"), + + UNKNOWN_ELEMENT("") + ; + + public static final String NS_URI = "urn:keycloak:saml:adapter"; + + private final QName qName; + + private KeycloakSamlAdapterV1QNames(String localName) { + this(NS_URI, localName); + } + + private KeycloakSamlAdapterV1QNames(HasQName source) { + this.qName = source.getQName(); + } + + private KeycloakSamlAdapterV1QNames(String nsUri, String localName) { + this.qName = new QName(nsUri == null ? null : nsUri, 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/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysParser.java new file mode 100644 index 0000000000..73e59e36cd --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysParser.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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.Key; +import org.keycloak.adapters.saml.config.SP; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import java.util.LinkedList; +import java.util.List; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class KeysParser extends AbstractKeycloakSamlAdapterV1Parser> { + + private static final KeysParser INSTANCE = new KeysParser(); + + private KeysParser() { + super(KeycloakSamlAdapterV1QNames.KEYS); + } + + public static KeysParser getInstance() { + return INSTANCE; + } + + @Override + protected List instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new LinkedList<>(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, List target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case KEY: + target.add(KeyParser.getInstance().parse(xmlEventReader)); + break; + } + } +} 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 deleted file mode 100755 index 13afbdbafa..0000000000 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeysXmlParser.java +++ /dev/null @@ -1,72 +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.adapters.saml.config.parsers; - -import org.keycloak.adapters.saml.config.Key; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; -import org.keycloak.saml.common.util.StaxParserUtil; - -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 java.util.LinkedList; -import java.util.List; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class KeysXmlParser extends AbstractParser { - - @Override - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_ELEMENT); - List keys = new LinkedList<>(); - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getElementName(endElement); - if (endElementName.equals(ConfigXmlConstants.KEYS_ELEMENT)) - break; - else - throw logger.parserUnknownEndElement(endElementName, xmlEvent.getLocation()); - } - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getElementName(startElement); - if (tag.equals(ConfigXmlConstants.KEY_ELEMENT)) { - KeyXmlParser parser = new KeyXmlParser(); - Key key = (Key)parser.parse(xmlEventReader); - keys.add(key); - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } - - } - return keys; - } - -} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/PrincipalNameMappingParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/PrincipalNameMappingParser.java new file mode 100644 index 0000000000..74383824e7 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/PrincipalNameMappingParser.java @@ -0,0 +1,56 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.SP; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class PrincipalNameMappingParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final PrincipalNameMappingParser INSTANCE = new PrincipalNameMappingParser(); + + private PrincipalNameMappingParser() { + super(KeycloakSamlAdapterV1QNames.PRINCIPAL_NAME_MAPPING); + } + + public static PrincipalNameMappingParser getInstance() { + return INSTANCE; + } + + @Override + protected SP.PrincipalNameMapping instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping(); + + mapping.setPolicy(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_POLICY)); + mapping.setAttributeName(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ATTRIBUTE)); + + return mapping; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, SP.PrincipalNameMapping target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/RoleMappingParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/RoleMappingParser.java new file mode 100644 index 0000000000..ac4c8ab935 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/RoleMappingParser.java @@ -0,0 +1,57 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import java.util.HashSet; +import java.util.Set; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class RoleMappingParser extends AbstractKeycloakSamlAdapterV1Parser> { + + private static final RoleMappingParser INSTANCE = new RoleMappingParser(); + + private RoleMappingParser() { + super(KeycloakSamlAdapterV1QNames.ROLE_IDENTIFIERS); + } + + public static RoleMappingParser getInstance() { + return INSTANCE; + } + + @Override + protected Set instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + return new HashSet<>(); + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, Set target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case ATTRIBUTE: + target.add(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_NAME)); + break; + } + } +} 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 deleted file mode 100755 index 45739870dd..0000000000 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java +++ /dev/null @@ -1,177 +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.adapters.saml.config.parsers; - -import org.keycloak.adapters.saml.config.IDP; -import org.keycloak.adapters.saml.config.Key; -import org.keycloak.adapters.saml.config.SP; -import org.keycloak.common.util.StringPropertyReplacer; -import org.keycloak.saml.common.exceptions.ParsingException; -import org.keycloak.saml.common.parsers.AbstractParser; -import org.keycloak.saml.common.util.StaxParserUtil; - -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 java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class SPXmlParser extends AbstractParser { - - public static String getAttributeValue(StartElement startElement, String tag) { - String str = StaxParserUtil.getAttributeValue(startElement, tag); - if (str != null) - return StringPropertyReplacer.replaceProperties(str); - else - return str; - } - - public static int getIntegerAttributeValue(StartElement startElement, String tag, int defaultValue) { - String result = getAttributeValue(startElement, tag); - if (result == null) - return defaultValue; - return Integer.valueOf(result); - } - - public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) { - String result = getAttributeValue(startElement, tag); - if (result == null) - return defaultValue; - return Boolean.valueOf(result); - } - - public static boolean getBooleanAttributeValue(StartElement startElement, String tag) { - return getBooleanAttributeValue(startElement, tag, false); - } - - public static String getElementText(XMLEventReader xmlEventReader) throws ParsingException { - String result = StaxParserUtil.getElementText(xmlEventReader); - if (result != null) - result = StringPropertyReplacer.replaceProperties(result); - return result; - } - - @Override - public Object parse(XMLEventReader xmlEventReader) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.SP_ELEMENT); - SP sp = new SP(); - String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR); - if (entityID == null) { - throw new ParsingException("entityID must be set on SP"); - - } - sp.setEntityID(entityID); - sp.setSslPolicy(getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR)); - sp.setLogoutPage(getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR)); - sp.setNameIDPolicyFormat(getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR)); - sp.setForceAuthentication(getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR)); - sp.setIsPassive(getBooleanAttributeValue(startElement, ConfigXmlConstants.IS_PASSIVE_ATTR)); - sp.setAutodetectBearerOnly(getBooleanAttributeValue(startElement, ConfigXmlConstants.AUTODETECT_BEARER_ONLY_ATTR)); - sp.setTurnOffChangeSessionIdOnLogin(getBooleanAttributeValue(startElement, ConfigXmlConstants.TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN_ATTR)); - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getElementName(endElement); - if (endElementName.equals(ConfigXmlConstants.SP_ELEMENT)) - break; - else - continue; - } - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getElementName(startElement); - if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) { - KeysXmlParser parser = new KeysXmlParser(); - List keys = (List) parser.parse(xmlEventReader); - sp.setKeys(keys); - } else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) { - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - String policy = getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR); - if (policy == null) { - throw new ParsingException("PrincipalNameMapping element must have the policy attribute set"); - - } - String attribute = getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR); - SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping(); - mapping.setPolicy(policy); - mapping.setAttributeName(attribute); - sp.setPrincipalNameMapping(mapping); - - } else if (tag.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) { - parseRoleMapping(xmlEventReader, sp); - } else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) { - IDPXmlParser parser = new IDPXmlParser(); - IDP idp = (IDP) parser.parse(xmlEventReader); - sp.setIdp(idp); - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } - - } - return sp; - } - - protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException { - StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT); - Set roleAttributes = new HashSet<>(); - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); - if (xmlEvent == null) - break; - if (xmlEvent instanceof EndElement) { - EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader); - String endElementName = StaxParserUtil.getElementName(endElement); - if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) - break; - else - continue; - } - startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); - if (startElement == null) - break; - String tag = StaxParserUtil.getElementName(startElement); - if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) { - StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - String attributeValue = getAttributeValue(element, ConfigXmlConstants.NAME_ATTR); - if (attributeValue == null) { - throw new ParsingException("RoleMapping Attribute element must have the name attribute set"); - - } - roleAttributes.add(attributeValue); - } else { - StaxParserUtil.bypassElementBlock(xmlEventReader, tag); - } - - } - sp.setRoleAttributes(roleAttributes); - } - -} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SingleLogoutServiceParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SingleLogoutServiceParser.java new file mode 100644 index 0000000000..7340058f0e --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SingleLogoutServiceParser.java @@ -0,0 +1,62 @@ +/* + * 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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.IDP; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SingleLogoutServiceParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final SingleLogoutServiceParser INSTANCE = new SingleLogoutServiceParser(); + + private SingleLogoutServiceParser() { + super(KeycloakSamlAdapterV1QNames.SINGLE_LOGOUT_SERVICE); + } + + public static SingleLogoutServiceParser getInstance() { + return INSTANCE; + } + + @Override + protected IDP.SingleLogoutService instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final IDP.SingleLogoutService slo = new IDP.SingleLogoutService(); + + slo.setSignRequest(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGN_REQUEST)); + slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_RESPONSE_SIGNATURE)); + slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_REQUEST_SIGNATURE)); + slo.setRequestBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_REQUEST_BINDING)); + slo.setResponseBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_RESPONSE_BINDING)); + slo.setSignResponse(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGN_RESPONSE)); + slo.setPostBindingUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_POST_BINDING_URL)); + slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_REDIRECT_BINDING_URL)); + + return slo; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, IDP.SingleLogoutService target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SingleSignOnServiceParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SingleSignOnServiceParser.java new file mode 100644 index 0000000000..52306c3d3e --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SingleSignOnServiceParser.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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.IDP; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SingleSignOnServiceParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final SingleSignOnServiceParser INSTANCE = new SingleSignOnServiceParser(); + + private SingleSignOnServiceParser() { + super(KeycloakSamlAdapterV1QNames.SINGLE_SIGN_ON_SERVICE); + } + + public static SingleSignOnServiceParser getInstance() { + return INSTANCE; + } + + @Override + protected IDP.SingleSignOnService instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final IDP.SingleSignOnService sso = new IDP.SingleSignOnService(); + + sso.setSignRequest(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SIGN_REQUEST)); + sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_RESPONSE_SIGNATURE)); + sso.setValidateAssertionSignature(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_VALIDATE_ASSERTION_SIGNATURE)); + sso.setRequestBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_REQUEST_BINDING)); + sso.setResponseBinding(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_RESPONSE_BINDING)); + sso.setBindingUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_BINDING_URL)); + sso.setAssertionConsumerServiceUrl(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ASSERTION_CONSUMER_SERVICE_URL)); + + return sso; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, IDP.SingleSignOnService target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SpParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SpParser.java new file mode 100644 index 0000000000..eac4db8b50 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SpParser.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.adapters.saml.config.parsers; + +import org.keycloak.adapters.saml.config.SP; +import org.keycloak.saml.common.exceptions.ParsingException; +import org.keycloak.saml.common.util.StaxParserUtil; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.StartElement; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SpParser extends AbstractKeycloakSamlAdapterV1Parser { + + private static final SpParser INSTANCE = new SpParser(); + + private SpParser() { + super(KeycloakSamlAdapterV1QNames.SP); + } + + public static SpParser getInstance() { + return INSTANCE; + } + + @Override + protected SP instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException { + final SP sp = new SP(); + + sp.setEntityID(StaxParserUtil.getRequiredAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_ENTITY_ID)); + + sp.setSslPolicy(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_SSL_POLICY)); + sp.setLogoutPage(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_LOGOUT_PAGE)); + sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_NAME_ID_POLICY_FORMAT)); + sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_FORCE_AUTHENTICATION)); + sp.setIsPassive(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_IS_PASSIVE)); + sp.setAutodetectBearerOnly(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_AUTODETECT_BEARER_ONLY)); + sp.setTurnOffChangeSessionIdOnLogin(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN)); + + return sp; + } + + @Override + protected void processSubElement(XMLEventReader xmlEventReader, SP target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException { + switch (element) { + case KEYS: + target.setKeys(KeysParser.getInstance().parse(xmlEventReader)); + break; + + case PRINCIPAL_NAME_MAPPING: + target.setPrincipalNameMapping(PrincipalNameMappingParser.getInstance().parse(xmlEventReader)); + break; + + case ROLE_IDENTIFIERS: + target.setRoleAttributes(RoleMappingParser.getInstance().parse(xmlEventReader)); + break; + + case IDP: + target.setIdp(IdpParser.getInstance().parse(xmlEventReader)); + break; + } + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/descriptor/parsers/SamlDescriptorIDPKeysExtractor.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/descriptor/parsers/SamlDescriptorIDPKeysExtractor.java index b8d5d6658a..cedad34c80 100644 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/descriptor/parsers/SamlDescriptorIDPKeysExtractor.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/descriptor/parsers/SamlDescriptorIDPKeysExtractor.java @@ -35,6 +35,7 @@ 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.DocumentUtil; +import org.keycloak.saml.processing.core.parsers.saml.xmldsig.XmlDSigQNames; import org.keycloak.saml.processing.core.util.NamespaceContext; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -88,7 +89,7 @@ public class SamlDescriptorIDPKeysExtractor { } private KeyInfo processKeyDescriptor(Element keyDescriptor) throws MarshalException { - NodeList childNodes = keyDescriptor.getElementsByTagNameNS(JBossSAMLURIConstants.XMLDSIG_NSURI.get(), JBossSAMLConstants.KEY_INFO.get()); + NodeList childNodes = keyDescriptor.getElementsByTagNameNS(JBossSAMLURIConstants.XMLDSIG_NSURI.get(), XmlDSigQNames.KEY_INFO.getQName().getLocalPart()); if (childNodes.getLength() == 0) { return null; diff --git a/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java b/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java index 2c03ef884d..8676a028d5 100644 --- a/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java +++ b/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java @@ -14,7 +14,7 @@ import javax.xml.crypto.dsig.keyinfo.X509Data; import static org.hamcrest.CoreMatchers.*; import org.junit.Test; import static org.junit.Assert.*; -import org.keycloak.adapters.saml.config.parsers.ConfigXmlConstants; +import org.keycloak.adapters.saml.config.parsers.KeycloakSamlAdapterV1QNames; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.saml.common.exceptions.ParsingException; @@ -41,15 +41,15 @@ public class HttpAdapterUtilsTest { assertThat(res, notNullValue()); assertThat(res.keySet(), hasItems(KeyTypes.SIGNING.value())); - assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR), notNullValue()); - assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR).size(), equalTo(2)); + assertThat(res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()), notNullValue()); + assertThat(res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()).size(), equalTo(2)); KeyInfo ki; KeyName keyName; X509Data x509data; X509Certificate x509certificate; - ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(0); + ki = res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()).get(0); assertThat(ki.getContent().size(), equalTo(2)); assertThat((List) ki.getContent(), hasItem(instanceOf(X509Data.class))); assertThat((List) ki.getContent(), hasItem(instanceOf(KeyName.class))); @@ -63,7 +63,7 @@ public class HttpAdapterUtilsTest { assertThat(x509certificate, notNullValue()); assertThat(x509certificate.getSigAlgName(), equalTo("SHA256withRSA")); - ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(1); + ki = res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()).get(1); assertThat(ki.getContent().size(), equalTo(2)); assertThat((List) ki.getContent(), hasItem(instanceOf(X509Data.class))); assertThat((List) ki.getContent(), hasItem(instanceOf(KeyName.class))); diff --git a/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java b/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java index 88a96cb142..1a287435e6 100755 --- a/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java +++ b/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java @@ -30,6 +30,8 @@ import java.io.InputStream; import org.junit.Rule; import org.junit.rules.ExpectedException; import org.keycloak.saml.common.exceptions.ParsingException; +import java.io.IOException; +import org.hamcrest.Matchers; /** * @author Bill Burke @@ -67,7 +69,7 @@ public class KeycloakSamlAdapterXMLParserTest { @Test public void testValidationKeyInvalid() throws Exception { - InputStream schemaIs = KeycloakSamlAdapterXMLParser.class.getResourceAsStream(CURRENT_XSD_LOCATION); + InputStream schemaIs = KeycloakSamlAdapterV1Parser.class.getResourceAsStream(CURRENT_XSD_LOCATION); InputStream is = getClass().getResourceAsStream("keycloak-saml-invalid.xml"); assertNotNull(is); assertNotNull(schemaIs); @@ -77,12 +79,14 @@ public class KeycloakSamlAdapterXMLParserTest { } @Test - public void testXmlParser() throws Exception { - InputStream is = getClass().getResourceAsStream("keycloak-saml.xml"); - assertNotNull(is); - KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser(); + public void testParseSimpleFileNoNamespace() throws Exception { + KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-no-namespace.xml", KeycloakSamlAdapter.class); + } + + @Test + public void testXmlParserBaseFile() throws Exception { + KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml.xml", KeycloakSamlAdapter.class); - KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is); assertNotNull(config); assertEquals(1, config.getSps().size()); SP sp = config.getSps().get(0); @@ -121,7 +125,7 @@ public class KeycloakSamlAdapterXMLParserTest { assertEquals("POST", idp.getSingleSignOnService().getRequestBinding()); assertEquals("url", idp.getSingleSignOnService().getBindingUrl()); - assertTrue(idp.getSingleLogoutService().isSignRequest()); + assertFalse(idp.getSingleLogoutService().isSignRequest()); assertTrue(idp.getSingleLogoutService().isSignResponse()); assertTrue(idp.getSingleLogoutService().isValidateRequestSignature()); assertTrue(idp.getSingleLogoutService().isValidateResponseSignature()); @@ -135,14 +139,17 @@ public class KeycloakSamlAdapterXMLParserTest { assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem()); } + private T parseKeycloakSamlAdapterConfig(String fileName, Class targetClass) throws ParsingException, IOException { + try (InputStream is = getClass().getResourceAsStream(fileName)) { + KeycloakSamlAdapterParser parser = KeycloakSamlAdapterParser.getInstance(); + return targetClass.cast(parser.parse(is)); + } + } + @Test public void testXmlParserMultipleSigningKeys() throws Exception { - InputStream is = getClass().getResourceAsStream("keycloak-saml-multiple-signing-keys.xml"); - assertNotNull(is); - KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser(); - - KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is); + KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-multiple-signing-keys.xml", KeycloakSamlAdapter.class); assertNotNull(config); assertEquals(1, config.getSps().size()); SP sp = config.getSps().get(0); @@ -158,11 +165,7 @@ public class KeycloakSamlAdapterXMLParserTest { @Test public void testXmlParserHttpClientSettings() throws Exception { - InputStream is = getClass().getResourceAsStream("keycloak-saml-wth-http-client-settings.xml"); - assertNotNull(is); - KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser(); - - KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is); + KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-wth-http-client-settings.xml", KeycloakSamlAdapter.class); assertNotNull(config); assertEquals(1, config.getSps().size()); SP sp = config.getSps().get(0); @@ -178,4 +181,66 @@ public class KeycloakSamlAdapterXMLParserTest { assertThat(idp.getHttpClientConfig().isAllowAnyHostname(), is(true)); assertThat(idp.getHttpClientConfig().isDisableTrustManager(), is(true)); } + + @Test + public void testXmlParserSystemPropertiesNoPropertiesSet() throws Exception { + KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-properties.xml", KeycloakSamlAdapter.class); + assertNotNull(config); + assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); + SP sp = config.getSps().get(0); + IDP idp = sp.getIdp(); + + assertThat(sp.getEntityID(), is("sp")); + assertThat(sp.getSslPolicy(), is("${keycloak-saml-properties.sslPolicy}")); + + assertThat(idp.isSignaturesRequired(), is(false)); + + assertThat(idp.getSingleLogoutService().isSignRequest(), is(true)); + assertThat(idp.getSingleLogoutService().isSignResponse(), is(false)); + + assertThat(idp.getSingleSignOnService().isSignRequest(), is(true)); + assertThat(idp.getSingleSignOnService().isValidateResponseSignature(), is(true)); + + // These should take default from IDP.signaturesRequired + assertThat(idp.getSingleLogoutService().isValidateRequestSignature(), is(false)); + assertThat(idp.getSingleLogoutService().isValidateResponseSignature(), is(false)); + + assertThat(idp.getSingleSignOnService().isValidateAssertionSignature(), is(false)); + } + + @Test + public void testXmlParserSystemPropertiesWithPropertiesSet() throws Exception { + try { + System.setProperty("keycloak-saml-properties.entityID", "meid"); + System.setProperty("keycloak-saml-properties.sslPolicy", "INTERNAL"); + System.setProperty("keycloak-saml-properties.signaturesRequired", "true"); + + KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-properties.xml", KeycloakSamlAdapter.class); + assertNotNull(config); + assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); + SP sp = config.getSps().get(0); + IDP idp = sp.getIdp(); + + assertThat(sp.getEntityID(), is("meid")); + assertThat(sp.getSslPolicy(), is("INTERNAL")); + assertThat(idp.isSignaturesRequired(), is(true)); + + assertThat(idp.getSingleLogoutService().isSignRequest(), is(true)); + assertThat(idp.getSingleLogoutService().isSignResponse(), is(false)); + + assertThat(idp.getSingleSignOnService().isSignRequest(), is(true)); + assertThat(idp.getSingleSignOnService().isValidateResponseSignature(), is(true)); + + // These should take default from IDP.signaturesRequired + assertThat(idp.getSingleLogoutService().isValidateRequestSignature(), is(true)); + assertThat(idp.getSingleLogoutService().isValidateResponseSignature(), is(true)); + + // This is false by default + assertThat(idp.getSingleSignOnService().isValidateAssertionSignature(), is(false)); + } finally { + System.clearProperty("keycloak-saml-properties.entityID"); + System.clearProperty("keycloak-saml-properties.sslPolicy"); + System.clearProperty("keycloak-saml-properties.signaturesRequired"); + } + } } diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-no-namespace.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-no-namespace.xml new file mode 100644 index 0000000000..87aea81100 --- /dev/null +++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-no-namespace.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + private pem + + + public pem + + + + + + + + + + + + + + + cert pem + + + + + + diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-properties.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-properties.xml new file mode 100644 index 0000000000..a54bf36212 --- /dev/null +++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-properties.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + private pem + + + public pem + + + + + + + + + + + + + + + cert pem + + + + + + diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml.xml index 31f62e5856..e59d8f846b 100755 --- a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml.xml +++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml.xml @@ -57,7 +57,7 @@ implements StaxParser { protected static final PicketLinkLogger LOGGER = PicketLinkLoggerFactory.getLogger(); - private final QName expectedStartElement; + protected final QName expectedStartElement; private final E unknownElement; public AbstractStaxParser(QName expectedStartElement, E unknownElement) { @@ -51,7 +51,8 @@ public abstract class AbstractStaxParser implements StaxParser { // Get the start element and validate it is the expected one StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); - StaxParserUtil.validate(startElement, expectedStartElement); + final QName actualQName = startElement.getName(); + validateStartElement(startElement); T target = instantiateElement(xmlEventReader, startElement); // STATE: Start element has been read. @@ -76,7 +77,7 @@ public abstract class AbstractStaxParser implements StaxParser { } // If end element corresponding to this start element, stop processing. - if (Objects.equals(qName, expectedStartElement)) { + if (Objects.equals(qName, actualQName)) { // consume the end element and finish parsing of this tag StaxParserUtil.advance(xmlEventReader); break; @@ -105,13 +106,21 @@ public abstract class AbstractStaxParser implements StaxParser { // 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)) { + if (Objects.equals(actualQName, currentSubelement) || isUnknownElement(token)) { currentSubelement = null; } } return target; } + /** + * Validates that the given startElement has the expected properties (namely {@link QName} matches the expected one). + * @param startElement + * @return + */ + protected void validateStartElement(StartElement startElement) { + StaxParserUtil.validate(startElement, expectedStartElement); + } protected boolean isUnknownElement(E token) { return token == null || Objects.equals(token, unknownElement); 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 1fc338cea3..4f8a9a8614 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 @@ -16,6 +16,7 @@ */ package org.keycloak.saml.common.util; +import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.saml.common.ErrorCodes; import org.keycloak.saml.common.PicketLinkLogger; import org.keycloak.saml.common.PicketLinkLoggerFactory; @@ -189,6 +190,23 @@ public class StaxParserUtil { return value == null ? null : trim(value); } + /** + * Given an {@code Attribute}, get its trimmed value, replacing every occurrence of ${..} by corresponding system property value + * + * @param attribute + * + * @return + */ + public static String getAttributeValueRP(Attribute attribute) { + if (attribute == null) { + return null; + } + + final String value = attribute.getValue(); + + return value == null ? null : trim(StringPropertyReplacer.replaceProperties(value)); + } + /** * Get the Attribute value * @@ -214,6 +232,21 @@ public class StaxParserUtil { return getAttributeValue(startElement, attrName.getQName()); } + /** + * Get the Attribute value, replacing every occurrence of ${..} by corresponding system property value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @see StringPropertyReplacer#replaceProperties(java.lang.String) + * + * @return + */ + public static String getAttributeValueRP(StartElement startElement, HasQName attrName) { + final String value = getAttributeValue(startElement, attrName.getQName()); + return value == null ? null : StringPropertyReplacer.replaceProperties(value); + } + /** * Get the Attribute value * @@ -269,6 +302,20 @@ public class StaxParserUtil { return value == null ? null : Integer.valueOf(value); } + /** + * Get the Attribute value, replacing every occurrence of ${..} by corresponding system property value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static Integer getIntegerAttributeValueRP(StartElement startElement, HasQName attrName) { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValueRP(attr); + return value == null ? null : Integer.valueOf(value); + } + /** * Get the Attribute value * @@ -283,6 +330,20 @@ public class StaxParserUtil { return value == null ? null : Boolean.valueOf(value); } + /** + * Get the Attribute value, replacing every occurrence of ${..} by corresponding system property value + * + * @param startElement + * @param tag localpart of the qname of the attribute + * + * @return + */ + public static Boolean getBooleanAttributeValueRP(StartElement startElement, HasQName attrName) { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValueRP(attr); + return value == null ? null : Boolean.valueOf(value); + } + /** * Get the Attribute value * @@ -322,6 +383,14 @@ public class StaxParserUtil { return StaxParserUtil.getAttributeValue(attr); } + public static String getRequiredAttributeValueRP(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.getAttributeValueRP(attr); + } + private static final String JDK_TRANSFORMER_PROPERTY = "picketlink.jdk.transformer"; /** 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 index 9a585f4030..3a92903726 100644 --- 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 @@ -53,6 +53,11 @@ public class QNameEnumLookup & HasQName> { this.qNameConstants = Collections.unmodifiableMap(q); } + /** + * Looks up the given {@code name} and returns the corresponding constant. + * @param name + * @return + */ public E from(QName name) { E c = qNameConstants.get(name); if (c == null) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java index ba8aac3985..21af1ce7f3 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java @@ -1204,7 +1204,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd Client client = ClientBuilder.newClient(); // Do not redirect client to login page if it's an XHR - WebTarget target = client.target(salesPostAutodetectServletPage.toString()); + WebTarget target = client.target(salesPostAutodetectServletPage.toString() + "/"); Response response = target.request().header("X-Requested-With", "XMLHttpRequest").get(); Assert.assertEquals(401, response.getStatus()); response.close();