KEYCLOAK-6470 Refactor SAML adapter parsers

This commit is contained in:
Hynek Mlnarik 2018-02-23 20:32:23 +01:00 committed by Hynek Mlnařík
parent d70e4740fc
commit 1f20c03afa
35 changed files with 1346 additions and 726 deletions

View file

@ -71,6 +71,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>

View file

@ -27,35 +27,36 @@ import org.keycloak.adapters.cloned.AdapterHttpClientConfig;
*/ */
public class IDP implements Serializable { public class IDP implements Serializable {
public static class SingleSignOnService implements Serializable { public static class SingleSignOnService implements Serializable {
private boolean signRequest; private Boolean signRequest;
private boolean validateResponseSignature; private Boolean validateResponseSignature;
private String requestBinding; private String requestBinding;
private String responseBinding; private String responseBinding;
private String bindingUrl; private String bindingUrl;
private String assertionConsumerServiceUrl; private String assertionConsumerServiceUrl;
private boolean validateAssertionSignature; private Boolean validateAssertionSignature;
private boolean signaturesRequired = false;
public boolean isSignRequest() { public boolean isSignRequest() {
return signRequest; return signRequest == null ? signaturesRequired : signRequest;
} }
public void setSignRequest(boolean signRequest) { public void setSignRequest(Boolean signRequest) {
this.signRequest = signRequest; this.signRequest = signRequest;
} }
public boolean isValidateResponseSignature() { public boolean isValidateResponseSignature() {
return validateResponseSignature; return validateResponseSignature == null ? signaturesRequired : validateResponseSignature;
} }
public void setValidateResponseSignature(boolean validateResponseSignature) { public void setValidateResponseSignature(Boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature; this.validateResponseSignature = validateResponseSignature;
} }
public boolean isValidateAssertionSignature() { public boolean isValidateAssertionSignature() {
return validateAssertionSignature; return validateAssertionSignature == null ? false : validateAssertionSignature;
} }
public void setValidateAssertionSignature(boolean validateAssertionSignature) { public void setValidateAssertionSignature(Boolean validateAssertionSignature) {
this.validateAssertionSignature = validateAssertionSignature; this.validateAssertionSignature = validateAssertionSignature;
} }
@ -90,47 +91,52 @@ public class IDP implements Serializable {
public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) { public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl; this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
} }
private void setSignaturesRequired(boolean signaturesRequired) {
this.signaturesRequired = signaturesRequired;
}
} }
public static class SingleLogoutService implements Serializable { public static class SingleLogoutService implements Serializable {
private boolean signRequest; private Boolean signRequest;
private boolean signResponse; private Boolean signResponse;
private boolean validateRequestSignature; private Boolean validateRequestSignature;
private boolean validateResponseSignature; private Boolean validateResponseSignature;
private String requestBinding; private String requestBinding;
private String responseBinding; private String responseBinding;
private String postBindingUrl; private String postBindingUrl;
private String redirectBindingUrl; private String redirectBindingUrl;
private boolean signaturesRequired = false;
public boolean isSignRequest() { public boolean isSignRequest() {
return signRequest; return signRequest == null ? signaturesRequired : signRequest;
} }
public void setSignRequest(boolean signRequest) { public void setSignRequest(Boolean signRequest) {
this.signRequest = signRequest; this.signRequest = signRequest;
} }
public boolean isSignResponse() { public boolean isSignResponse() {
return signResponse; return signResponse == null ? signaturesRequired : signResponse;
} }
public void setSignResponse(boolean signResponse) { public void setSignResponse(Boolean signResponse) {
this.signResponse = signResponse; this.signResponse = signResponse;
} }
public boolean isValidateRequestSignature() { public boolean isValidateRequestSignature() {
return validateRequestSignature; return validateRequestSignature == null ? signaturesRequired : validateRequestSignature;
} }
public void setValidateRequestSignature(boolean validateRequestSignature) { public void setValidateRequestSignature(Boolean validateRequestSignature) {
this.validateRequestSignature = validateRequestSignature; this.validateRequestSignature = validateRequestSignature;
} }
public boolean isValidateResponseSignature() { public boolean isValidateResponseSignature() {
return validateResponseSignature; return validateResponseSignature == null ? signaturesRequired : validateResponseSignature;
} }
public void setValidateResponseSignature(boolean validateResponseSignature) { public void setValidateResponseSignature(Boolean validateResponseSignature) {
this.validateResponseSignature = validateResponseSignature; this.validateResponseSignature = validateResponseSignature;
} }
@ -165,6 +171,10 @@ public class IDP implements Serializable {
public void setRedirectBindingUrl(String redirectBindingUrl) { public void setRedirectBindingUrl(String redirectBindingUrl) {
this.redirectBindingUrl = redirectBindingUrl; this.redirectBindingUrl = redirectBindingUrl;
} }
private void setSignaturesRequired(boolean signaturesRequired) {
this.signaturesRequired = signaturesRequired;
}
} }
public static class HttpClientConfig implements AdapterHttpClientConfig { public static class HttpClientConfig implements AdapterHttpClientConfig {
@ -258,6 +268,7 @@ public class IDP implements Serializable {
private SingleLogoutService singleLogoutService; private SingleLogoutService singleLogoutService;
private List<Key> keys; private List<Key> keys;
private AdapterHttpClientConfig httpClientConfig = new HttpClientConfig(); private AdapterHttpClientConfig httpClientConfig = new HttpClientConfig();
private boolean signaturesRequired = false;
public String getEntityID() { public String getEntityID() {
return entityID; return entityID;
@ -273,6 +284,9 @@ public class IDP implements Serializable {
public void setSingleSignOnService(SingleSignOnService singleSignOnService) { public void setSingleSignOnService(SingleSignOnService singleSignOnService) {
this.singleSignOnService = singleSignOnService; this.singleSignOnService = singleSignOnService;
if (singleSignOnService != null) {
singleSignOnService.setSignaturesRequired(signaturesRequired);
}
} }
public SingleLogoutService getSingleLogoutService() { public SingleLogoutService getSingleLogoutService() {
@ -281,6 +295,9 @@ public class IDP implements Serializable {
public void setSingleLogoutService(SingleLogoutService singleLogoutService) { public void setSingleLogoutService(SingleLogoutService singleLogoutService) {
this.singleLogoutService = singleLogoutService; this.singleLogoutService = singleLogoutService;
if (singleLogoutService != null) {
singleLogoutService.setSignaturesRequired(signaturesRequired);
}
} }
public List<Key> getKeys() { public List<Key> getKeys() {
@ -315,4 +332,12 @@ public class IDP implements Serializable {
this.httpClientConfig = httpClientConfig; this.httpClientConfig = httpClientConfig;
} }
public boolean isSignaturesRequired() {
return signaturesRequired;
}
public void setSignaturesRequired(boolean signaturesRequired) {
this.signaturesRequired = signaturesRequired;
}
} }

View file

@ -114,16 +114,16 @@ public class Key implements Serializable {
return signing; return signing;
} }
public void setSigning(boolean signing) { public void setSigning(Boolean signing) {
this.signing = signing; this.signing = signing != null && signing;
} }
public boolean isEncryption() { public boolean isEncryption() {
return encryption; return encryption;
} }
public void setEncryption(boolean encryption) { public void setEncryption(Boolean encryption) {
this.encryption = encryption; this.encryption = encryption != null && encryption;
} }
public KeyStoreConfig getKeystore() { public KeyStoreConfig getKeystore() {

View file

@ -26,13 +26,14 @@ import java.util.List;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class KeycloakSamlAdapter implements Serializable { public class KeycloakSamlAdapter implements Serializable {
private List<SP> sps = new LinkedList<>(); private final List<SP> sps = new LinkedList<>();
public List<SP> getSps() { public List<SP> getSps() {
return sps; return sps;
} }
public void setSps(List<SP> sps) { public void addSp(SP sp) {
this.sps = sps; sps.add(sp);
} }
} }

View file

@ -80,24 +80,24 @@ public class SP implements Serializable {
return forceAuthentication; return forceAuthentication;
} }
public void setForceAuthentication(boolean forceAuthentication) { public void setForceAuthentication(Boolean forceAuthentication) {
this.forceAuthentication = forceAuthentication; this.forceAuthentication = forceAuthentication != null && forceAuthentication;
} }
public boolean isIsPassive() { public boolean isIsPassive() {
return isPassive; return isPassive;
} }
public void setIsPassive(boolean isPassive) { public void setIsPassive(Boolean isPassive) {
this.isPassive = isPassive; this.isPassive = isPassive != null && isPassive;
} }
public boolean isTurnOffChangeSessionIdOnLogin() { public boolean isTurnOffChangeSessionIdOnLogin() {
return turnOffChangeSessionIdOnLogin; return turnOffChangeSessionIdOnLogin;
} }
public void setTurnOffChangeSessionIdOnLogin(boolean turnOffChangeSessionIdOnLogin) { public void setTurnOffChangeSessionIdOnLogin(Boolean turnOffChangeSessionIdOnLogin) {
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin; this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin != null && turnOffChangeSessionIdOnLogin;
} }
public List<Key> getKeys() { public List<Key> getKeys() {
@ -152,7 +152,7 @@ public class SP implements Serializable {
return autodetectBearerOnly; return autodetectBearerOnly;
} }
public void setAutodetectBearerOnly(boolean autodetectBearerOnly) { public void setAutodetectBearerOnly(Boolean autodetectBearerOnly) {
this.autodetectBearerOnly = autodetectBearerOnly; this.autodetectBearerOnly = autodetectBearerOnly != null && autodetectBearerOnly;
} }
} }

View file

@ -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<T> extends AbstractStaxParser<T, KeycloakSamlAdapterV1QNames> {
protected static final QNameEnumLookup<KeycloakSamlAdapterV1QNames> LOOKUP = new QNameEnumLookup(KeycloakSamlAdapterV1QNames.values());
private static final Set<String> 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);
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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";
}

View file

@ -59,7 +59,7 @@ public class DeploymentBuilder {
idp.setSingleSignOnService(sso); idp.setSingleSignOnService(sso);
idp.setSingleLogoutService(slo); idp.setSingleLogoutService(slo);
KeycloakSamlAdapter adapter = (KeycloakSamlAdapter)(new KeycloakSamlAdapterXMLParser().parse(xml)); KeycloakSamlAdapter adapter = (KeycloakSamlAdapter) KeycloakSamlAdapterParser.getInstance().parse(xml);
SP sp = adapter.getSps().get(0); SP sp = adapter.getSps().get(0);
deployment.setConfigured(true); deployment.setConfigured(true);
deployment.setEntityID(sp.getEntityID()); deployment.setEntityID(sp.getEntityID());

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class HttpClientParser extends AbstractKeycloakSamlAdapterV1Parser<HttpClientConfig> {
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 {
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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<Key> keys = (List<Key>)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;
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class IdpParser extends AbstractKeycloakSamlAdapterV1Parser<IDP> {
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;
}
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeyParser extends AbstractKeycloakSamlAdapterV1Parser<Key> {
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;
}
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeyStoreParser extends AbstractKeycloakSamlAdapterV1Parser<KeyStoreConfig> {
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;
}
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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;
}
}

View file

@ -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<QName, ParserFactory> PARSERS = new HashMap<QName, ParserFactory>();
// 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");
}
}

View file

@ -18,12 +18,9 @@
package org.keycloak.adapters.saml.config.parsers; package org.keycloak.adapters.saml.config.parsers;
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter; 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.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil; import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventReader;
import javax.xml.stream.events.StartElement; import javax.xml.stream.events.StartElement;
@ -31,28 +28,33 @@ import javax.xml.stream.events.StartElement;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class KeycloakSamlAdapterXMLParser extends AbstractParser { public class KeycloakSamlAdapterV1Parser extends AbstractKeycloakSamlAdapterV1Parser<KeycloakSamlAdapter> {
@Override private static final KeycloakSamlAdapterV1Parser INSTANCE = new KeycloakSamlAdapterV1Parser();
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 KeycloakSamlAdapterV1Parser() {
return adapter; 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);
}
}
} }

View file

@ -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);
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeysParser extends AbstractKeycloakSamlAdapterV1Parser<List<Key>> {
private static final KeysParser INSTANCE = new KeysParser();
private KeysParser() {
super(KeycloakSamlAdapterV1QNames.KEYS);
}
public static KeysParser getInstance() {
return INSTANCE;
}
@Override
protected List<Key> instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
return new LinkedList<>();
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, List<Key> target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case KEY:
target.add(KeyParser.getInstance().parse(xmlEventReader));
break;
}
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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<Key> 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;
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class PrincipalNameMappingParser extends AbstractKeycloakSamlAdapterV1Parser<SP.PrincipalNameMapping> {
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 {
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleMappingParser extends AbstractKeycloakSamlAdapterV1Parser<Set<String>> {
private static final RoleMappingParser INSTANCE = new RoleMappingParser();
private RoleMappingParser() {
super(KeycloakSamlAdapterV1QNames.ROLE_IDENTIFIERS);
}
public static RoleMappingParser getInstance() {
return INSTANCE;
}
@Override
protected Set<String> instantiateElement(XMLEventReader xmlEventReader, StartElement element) throws ParsingException {
return new HashSet<>();
}
@Override
protected void processSubElement(XMLEventReader xmlEventReader, Set<String> target, KeycloakSamlAdapterV1QNames element, StartElement elementDetail) throws ParsingException {
switch (element) {
case ATTRIBUTE:
target.add(StaxParserUtil.getRequiredAttributeValueRP(elementDetail, KeycloakSamlAdapterV1QNames.ATTR_NAME));
break;
}
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @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<Key> keys = (List<Key>) 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<String> 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);
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SingleLogoutServiceParser extends AbstractKeycloakSamlAdapterV1Parser<IDP.SingleLogoutService> {
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 {
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SingleSignOnServiceParser extends AbstractKeycloakSamlAdapterV1Parser<IDP.SingleSignOnService> {
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 {
}
}

View file

@ -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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SpParser extends AbstractKeycloakSamlAdapterV1Parser<SP> {
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;
}
}
}

View file

@ -35,6 +35,7 @@ import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.util.DocumentUtil; 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.keycloak.saml.processing.core.util.NamespaceContext;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
@ -88,7 +89,7 @@ public class SamlDescriptorIDPKeysExtractor {
} }
private KeyInfo processKeyDescriptor(Element keyDescriptor) throws MarshalException { 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) { if (childNodes.getLength() == 0) {
return null; return null;

View file

@ -14,7 +14,7 @@ import javax.xml.crypto.dsig.keyinfo.X509Data;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; 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.common.util.MultivaluedHashMap;
import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ParsingException;
@ -41,15 +41,15 @@ public class HttpAdapterUtilsTest {
assertThat(res, notNullValue()); assertThat(res, notNullValue());
assertThat(res.keySet(), hasItems(KeyTypes.SIGNING.value())); assertThat(res.keySet(), hasItems(KeyTypes.SIGNING.value()));
assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR), notNullValue()); assertThat(res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()), notNullValue());
assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR).size(), equalTo(2)); assertThat(res.get(KeycloakSamlAdapterV1QNames.ATTR_SIGNING.getQName().getLocalPart()).size(), equalTo(2));
KeyInfo ki; KeyInfo ki;
KeyName keyName; KeyName keyName;
X509Data x509data; X509Data x509data;
X509Certificate x509certificate; 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(ki.getContent().size(), equalTo(2));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class))); assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class))); assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));
@ -63,7 +63,7 @@ public class HttpAdapterUtilsTest {
assertThat(x509certificate, notNullValue()); assertThat(x509certificate, notNullValue());
assertThat(x509certificate.getSigAlgName(), equalTo("SHA256withRSA")); 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(ki.getContent().size(), equalTo(2));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class))); assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class))); assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));

View file

@ -30,6 +30,8 @@ import java.io.InputStream;
import org.junit.Rule; import org.junit.Rule;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ParsingException;
import java.io.IOException;
import org.hamcrest.Matchers;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -67,7 +69,7 @@ public class KeycloakSamlAdapterXMLParserTest {
@Test @Test
public void testValidationKeyInvalid() throws Exception { 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"); InputStream is = getClass().getResourceAsStream("keycloak-saml-invalid.xml");
assertNotNull(is); assertNotNull(is);
assertNotNull(schemaIs); assertNotNull(schemaIs);
@ -77,12 +79,14 @@ public class KeycloakSamlAdapterXMLParserTest {
} }
@Test @Test
public void testXmlParser() throws Exception { public void testParseSimpleFileNoNamespace() throws Exception {
InputStream is = getClass().getResourceAsStream("keycloak-saml.xml"); KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-no-namespace.xml", KeycloakSamlAdapter.class);
assertNotNull(is); }
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
@Test
public void testXmlParserBaseFile() throws Exception {
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml.xml", KeycloakSamlAdapter.class);
KeycloakSamlAdapter config = (KeycloakSamlAdapter)parser.parse(is);
assertNotNull(config); assertNotNull(config);
assertEquals(1, config.getSps().size()); assertEquals(1, config.getSps().size());
SP sp = config.getSps().get(0); SP sp = config.getSps().get(0);
@ -121,7 +125,7 @@ public class KeycloakSamlAdapterXMLParserTest {
assertEquals("POST", idp.getSingleSignOnService().getRequestBinding()); assertEquals("POST", idp.getSingleSignOnService().getRequestBinding());
assertEquals("url", idp.getSingleSignOnService().getBindingUrl()); assertEquals("url", idp.getSingleSignOnService().getBindingUrl());
assertTrue(idp.getSingleLogoutService().isSignRequest()); assertFalse(idp.getSingleLogoutService().isSignRequest());
assertTrue(idp.getSingleLogoutService().isSignResponse()); assertTrue(idp.getSingleLogoutService().isSignResponse());
assertTrue(idp.getSingleLogoutService().isValidateRequestSignature()); assertTrue(idp.getSingleLogoutService().isValidateRequestSignature());
assertTrue(idp.getSingleLogoutService().isValidateResponseSignature()); assertTrue(idp.getSingleLogoutService().isValidateResponseSignature());
@ -135,14 +139,17 @@ public class KeycloakSamlAdapterXMLParserTest {
assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem()); assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem());
} }
private <T> T parseKeycloakSamlAdapterConfig(String fileName, Class<T> targetClass) throws ParsingException, IOException {
try (InputStream is = getClass().getResourceAsStream(fileName)) {
KeycloakSamlAdapterParser parser = KeycloakSamlAdapterParser.getInstance();
return targetClass.cast(parser.parse(is));
}
}
@Test @Test
public void testXmlParserMultipleSigningKeys() throws Exception { public void testXmlParserMultipleSigningKeys() throws Exception {
InputStream is = getClass().getResourceAsStream("keycloak-saml-multiple-signing-keys.xml"); KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-multiple-signing-keys.xml", KeycloakSamlAdapter.class);
assertNotNull(is);
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
assertNotNull(config); assertNotNull(config);
assertEquals(1, config.getSps().size()); assertEquals(1, config.getSps().size());
SP sp = config.getSps().get(0); SP sp = config.getSps().get(0);
@ -158,11 +165,7 @@ public class KeycloakSamlAdapterXMLParserTest {
@Test @Test
public void testXmlParserHttpClientSettings() throws Exception { public void testXmlParserHttpClientSettings() throws Exception {
InputStream is = getClass().getResourceAsStream("keycloak-saml-wth-http-client-settings.xml"); KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-wth-http-client-settings.xml", KeycloakSamlAdapter.class);
assertNotNull(is);
KeycloakSamlAdapterXMLParser parser = new KeycloakSamlAdapterXMLParser();
KeycloakSamlAdapter config = (KeycloakSamlAdapter) parser.parse(is);
assertNotNull(config); assertNotNull(config);
assertEquals(1, config.getSps().size()); assertEquals(1, config.getSps().size());
SP sp = config.getSps().get(0); SP sp = config.getSps().get(0);
@ -178,4 +181,66 @@ public class KeycloakSamlAdapterXMLParserTest {
assertThat(idp.getHttpClientConfig().isAllowAnyHostname(), is(true)); assertThat(idp.getHttpClientConfig().isAllowAnyHostname(), is(true));
assertThat(idp.getHttpClientConfig().isDisableTrustManager(), 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");
}
}
} }

View file

@ -0,0 +1,74 @@
<!--
~ 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.
-->
<keycloak-saml-adapter>
<SP entityID="${keycloak-saml-properties.entityID:sp}"
sslPolicy="EXTERNAL"
nameIDPolicyFormat="format"
forceAuthentication="true"
isPassive="true">
<Keys>
<Key signing="true">
<KeyStore file="file" resource="cp" password="pw">
<PrivateKey alias="private alias" password="private pw"/>
<Certificate alias="cert alias"/>
</KeyStore>
</Key>
<Key encryption="true">
<PrivateKeyPem>
private pem
</PrivateKeyPem>
<PublicKeyPem>
public pem
</PublicKeyPem>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
<RoleIdentifiers>
<Attribute name="member"/>
</RoleIdentifiers>
<IDP entityID="idp"
signatureAlgorithm="RSA_SHA256"
signatureCanonicalizationMethod="canon"
signaturesRequired="true"
>
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
bindingUrl="url"
/>
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signResponse="true"
requestBinding="REDIRECT"
responseBinding="POST"
postBindingUrl="posturl"
redirectBindingUrl="redirecturl"
/>
<Keys>
<Key signing="true">
<CertificatePem>
cert pem
</CertificatePem>
</Key>
</Keys>
</IDP>
</SP>
</keycloak-saml-adapter>

View file

@ -0,0 +1,74 @@
<!--
~ 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.
-->
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_9.xsd">
<SP entityID="${keycloak-saml-properties.entityID:sp}"
sslPolicy="${keycloak-saml-properties.sslPolicy}"
nameIDPolicyFormat="format"
forceAuthentication="true"
isPassive="true">
<Keys>
<Key signing="true">
<KeyStore file="file" resource="cp" password="pw">
<PrivateKey alias="private alias" password="private pw"/>
<Certificate alias="cert alias"/>
</KeyStore>
</Key>
<Key encryption="true">
<PrivateKeyPem>
private pem
</PrivateKeyPem>
<PublicKeyPem>
public pem
</PublicKeyPem>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
<RoleIdentifiers>
<Attribute name="member"/>
</RoleIdentifiers>
<IDP entityID="idp"
signatureAlgorithm="RSA_SHA256"
signatureCanonicalizationMethod="canon"
signaturesRequired="${keycloak-saml-properties.signaturesRequired:false}"
>
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
bindingUrl="url"
/>
<SingleLogoutService
signRequest="true"
signResponse="false"
requestBinding="REDIRECT"
responseBinding="POST"
postBindingUrl="posturl"
redirectBindingUrl="redirecturl"
/>
<Keys>
<Key signing="true">
<CertificatePem>
cert pem
</CertificatePem>
</Key>
</Keys>
</IDP>
</SP>
</keycloak-saml-adapter>

View file

@ -57,7 +57,7 @@
<SingleLogoutService <SingleLogoutService
validateRequestSignature="true" validateRequestSignature="true"
validateResponseSignature="true" validateResponseSignature="true"
signRequest="true" signRequest="false"
signResponse="true" signResponse="true"
requestBinding="REDIRECT" requestBinding="REDIRECT"
responseBinding="POST" responseBinding="POST"

View file

@ -37,7 +37,7 @@ import javax.xml.stream.events.XMLEvent;
public abstract class AbstractStaxParser<T, E> implements StaxParser { public abstract class AbstractStaxParser<T, E> implements StaxParser {
protected static final PicketLinkLogger LOGGER = PicketLinkLoggerFactory.getLogger(); protected static final PicketLinkLogger LOGGER = PicketLinkLoggerFactory.getLogger();
private final QName expectedStartElement; protected final QName expectedStartElement;
private final E unknownElement; private final E unknownElement;
public AbstractStaxParser(QName expectedStartElement, E unknownElement) { public AbstractStaxParser(QName expectedStartElement, E unknownElement) {
@ -51,7 +51,8 @@ public abstract class AbstractStaxParser<T, E> implements StaxParser {
// Get the start element and validate it is the expected one // Get the start element and validate it is the expected one
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, expectedStartElement); final QName actualQName = startElement.getName();
validateStartElement(startElement);
T target = instantiateElement(xmlEventReader, startElement); T target = instantiateElement(xmlEventReader, startElement);
// STATE: Start element has been read. // STATE: Start element has been read.
@ -76,7 +77,7 @@ public abstract class AbstractStaxParser<T, E> implements StaxParser {
} }
// If end element corresponding to this start element, stop processing. // 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 // consume the end element and finish parsing of this tag
StaxParserUtil.advance(xmlEventReader); StaxParserUtil.advance(xmlEventReader);
break; break;
@ -105,13 +106,21 @@ public abstract class AbstractStaxParser<T, E> implements StaxParser {
// In case of recursive nesting the same element, the corresponding end element MUST be handled // 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. // 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; currentSubelement = null;
} }
} }
return target; 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) { protected boolean isUnknownElement(E token) {
return token == null || Objects.equals(token, unknownElement); return token == null || Objects.equals(token, unknownElement);

View file

@ -16,6 +16,7 @@
*/ */
package org.keycloak.saml.common.util; package org.keycloak.saml.common.util;
import org.keycloak.common.util.StringPropertyReplacer;
import org.keycloak.saml.common.ErrorCodes; import org.keycloak.saml.common.ErrorCodes;
import org.keycloak.saml.common.PicketLinkLogger; import org.keycloak.saml.common.PicketLinkLogger;
import org.keycloak.saml.common.PicketLinkLoggerFactory; import org.keycloak.saml.common.PicketLinkLoggerFactory;
@ -189,6 +190,23 @@ public class StaxParserUtil {
return value == null ? null : trim(value); 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 * Get the Attribute value
* *
@ -214,6 +232,21 @@ public class StaxParserUtil {
return getAttributeValue(startElement, attrName.getQName()); 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 * Get the Attribute value
* *
@ -269,6 +302,20 @@ public class StaxParserUtil {
return value == null ? null : Integer.valueOf(value); 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 * Get the Attribute value
* *
@ -283,6 +330,20 @@ public class StaxParserUtil {
return value == null ? null : Boolean.valueOf(value); 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 * Get the Attribute value
* *
@ -322,6 +383,14 @@ public class StaxParserUtil {
return StaxParserUtil.getAttributeValue(attr); 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"; private static final String JDK_TRANSFORMER_PROPERTY = "picketlink.jdk.transformer";
/** /**

View file

@ -53,6 +53,11 @@ public class QNameEnumLookup<E extends Enum<E> & HasQName> {
this.qNameConstants = Collections.unmodifiableMap(q); this.qNameConstants = Collections.unmodifiableMap(q);
} }
/**
* Looks up the given {@code name} and returns the corresponding constant.
* @param name
* @return
*/
public E from(QName name) { public E from(QName name) {
E c = qNameConstants.get(name); E c = qNameConstants.get(name);
if (c == null) { if (c == null) {

View file

@ -1204,7 +1204,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
Client client = ClientBuilder.newClient(); Client client = ClientBuilder.newClient();
// Do not redirect client to login page if it's an XHR // 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(); Response response = target.request().header("X-Requested-With", "XMLHttpRequest").get();
Assert.assertEquals(401, response.getStatus()); Assert.assertEquals(401, response.getStatus());
response.close(); response.close();