keycloak-uncached
Changes
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java 2(+1 -1)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java 7(+3 -4)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java 24(+14 -10)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java 2(+2 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java 12(+0 -12)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java 12(+1 -11)
saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java 43(+41 -2)
saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml 7(+7 -0)
Details
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
index 84764e3..956ab86 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java
@@ -134,6 +134,8 @@ public class IDP implements Serializable {
}
private String entityID;
+ private String signatureAlgorithm;
+ private String signatureCanonicalizationMethod;
private SingleSignOnService singleSignOnService;
private SingleLogoutService singleLogoutService;
private List<Key> keys;
@@ -169,4 +171,21 @@ public class IDP implements Serializable {
public void setKeys(List<Key> keys) {
this.keys = keys;
}
+
+ public String getSignatureAlgorithm() {
+ return signatureAlgorithm;
+ }
+
+ public void setSignatureAlgorithm(String signatureAlgorithm) {
+ this.signatureAlgorithm = signatureAlgorithm;
+ }
+
+ public String getSignatureCanonicalizationMethod() {
+ return signatureCanonicalizationMethod;
+ }
+
+ public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
+ this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
+ }
+
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
index 0e0542c..fdb8284 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -39,10 +39,10 @@ public class ConfigXmlConstants {
public static final String ROLE_MAPPING_ELEMENT = "RoleMapping";
public static final String ATTRIBUTE_ELEMENT = "Attribute";
- public static final String FRIENDLY_ATTRIBUTE_ELEMENT = "FriendlyAttribute";
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";
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
index b72cb41..0421fda 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -41,10 +41,10 @@ public class DeploymentBuilder {
deployment.setForceAuthentication(sp.isForceAuthentication());
deployment.setNameIDPolicyFormat(sp.getNameIDPolicyFormat());
deployment.setLogoutPage(sp.getLogoutPage());
- deployment.setSignatureCanonicalizationMethod(sp.getSignatureCanonicalizationMethod());
+ deployment.setSignatureCanonicalizationMethod(sp.getIdp().getSignatureCanonicalizationMethod());
deployment.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256);
- if (sp.getSignatureAlgorithm() != null) {
- deployment.setSignatureAlgorithm(SignatureAlgorithm.valueOf(sp.getSignatureAlgorithm()));
+ if (sp.getIdp().getSignatureAlgorithm() != null) {
+ deployment.setSignatureAlgorithm(SignatureAlgorithm.valueOf(sp.getIdp().getSignatureAlgorithm()));
}
if (sp.getPrincipalNameMapping() != null) {
SamlDeployment.PrincipalNamePolicy policy = SamlDeployment.PrincipalNamePolicy.valueOf(sp.getPrincipalNameMapping().getPolicy());
@@ -52,7 +52,6 @@ public class DeploymentBuilder {
deployment.setPrincipalAttributeName(sp.getPrincipalNameMapping().getAttributeName());
}
deployment.setRoleAttributeNames(sp.getRoleAttributes());
- deployment.setRoleFriendlyAttributeNames(sp.getRoleFriendlyAttributes());
if (sp.getSslPolicy() != null) {
SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy());
deployment.setSslRequired(ssl);
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
index 96485d5..fc08e44 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
@@ -30,6 +30,10 @@ public class IDPXmlParser extends AbstractParser {
}
idp.setEntityID(entityID);
+
+ boolean signaturesRequired = StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
+ idp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
+ idp.setSignatureAlgorithm(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -47,11 +51,11 @@ public class IDPXmlParser extends AbstractParser {
break;
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.SINGLE_SIGN_ON_SERVICE_ELEMENT)) {
- IDP.SingleSignOnService sso = parseSingleSignOnService(xmlEventReader);
+ IDP.SingleSignOnService sso = parseSingleSignOnService(xmlEventReader, signaturesRequired);
idp.setSingleSignOnService(sso);
} else if (tag.equals(ConfigXmlConstants.SINGLE_LOGOUT_SERVICE_ELEMENT)) {
- IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader);
+ IDP.SingleLogoutService slo = parseSingleLogoutService(xmlEventReader, signaturesRequired);
idp.setSingleLogoutService(slo);
} else if (tag.equals(ConfigXmlConstants.KEYS_ELEMENT)) {
@@ -66,25 +70,25 @@ public class IDPXmlParser extends AbstractParser {
return idp;
}
- protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader) throws ParsingException {
+ protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
- slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
- slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR));
+ slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+ slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
+ slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
- slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR));
+ slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
slo.setPostBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
return slo;
}
- protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader) throws ParsingException {
+ protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR));
- sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR));
+ sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+ sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
index 787af69..6fbd8d0 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
@@ -7,6 +7,8 @@ import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
index 089dc78..f010470 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
@@ -37,8 +37,6 @@ public class SPXmlParser extends AbstractParser {
sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
- sp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
- sp.setSignatureAlgorithm(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
@@ -91,7 +89,6 @@ public class SPXmlParser extends AbstractParser {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_MAPPING_ELEMENT);
Set<String> roleAttributes = new HashSet<>();
- Set<String> roleFriendlyAttributes = new HashSet<>();
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -116,21 +113,12 @@ public class SPXmlParser extends AbstractParser {
}
roleAttributes.add(attributeValue);
- } else if (tag.equals(ConfigXmlConstants.FRIENDLY_ATTRIBUTE_ELEMENT)) {
- StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
- if (attributeValue == null) {
- throw new ParsingException("RoleMapping FriendlyAttribute element must have the name attribute set");
-
- }
- roleFriendlyAttributes.add(attributeValue);
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
}
sp.setRoleAttributes(roleAttributes);
- sp.setRoleFriendlyAttributes(roleFriendlyAttributes);
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
index bd48ba1..fadfe21 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/SP.java
@@ -41,9 +41,6 @@ public class SP implements Serializable {
private String nameIDPolicyFormat;
private PrincipalNameMapping principalNameMapping;
private Set<String> roleAttributes;
- private Set<String> roleFriendlyAttributes;
- private String signatureAlgorithm;
- private String signatureCanonicalizationMethod;
private IDP idp;
public String getEntityID() {
@@ -102,14 +99,6 @@ public class SP implements Serializable {
this.roleAttributes = roleAttributes;
}
- public Set<String> getRoleFriendlyAttributes() {
- return roleFriendlyAttributes;
- }
-
- public void setRoleFriendlyAttributes(Set<String> roleFriendlyAttributes) {
- this.roleFriendlyAttributes = roleFriendlyAttributes;
- }
-
public IDP getIdp() {
return idp;
}
@@ -126,19 +115,4 @@ public class SP implements Serializable {
this.logoutPage = logoutPage;
}
- public String getSignatureAlgorithm() {
- return signatureAlgorithm;
- }
-
- public void setSignatureAlgorithm(String signatureAlgorithm) {
- this.signatureAlgorithm = signatureAlgorithm;
- }
-
- public String getSignatureCanonicalizationMethod() {
- return signatureCanonicalizationMethod;
- }
-
- public void setSignatureCanonicalizationMethod(String signatureCanonicalizationMethod) {
- this.signatureCanonicalizationMethod = signatureCanonicalizationMethod;
- }
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
index 28a4ead..4aa54d6 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java
@@ -210,7 +210,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
private KeyPair signingKeyPair;
private String assertionConsumerServiceUrl;
private Set<String> roleAttributeNames;
- private Set<String> roleFriendlyAttributeNames;
private PrincipalNamePolicy principalNamePolicy = PrincipalNamePolicy.FROM_NAME_ID;
private String principalAttributeName;
private String logoutPage;
@@ -268,12 +267,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
return roleAttributeNames;
}
- @Override
- public Set<String> getRoleAttributeFriendlyNames() {
- return roleFriendlyAttributeNames;
- }
-
- @Override
+ @Override
public PrincipalNamePolicy getPrincipalNamePolicy() {
return principalNamePolicy;
}
@@ -323,10 +317,6 @@ public class DefaultSamlDeployment implements SamlDeployment {
this.roleAttributeNames = roleAttributeNames;
}
- public void setRoleFriendlyAttributeNames(Set<String> roleFriendlyAttributeNames) {
- this.roleFriendlyAttributeNames = roleFriendlyAttributeNames;
- }
-
public void setPrincipalNamePolicy(PrincipalNamePolicy principalNamePolicy) {
this.principalNamePolicy = principalNamePolicy;
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
index ae7eb4b..bebb506 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
@@ -354,7 +354,7 @@ public abstract class SamlAuthenticator {
}
protected boolean isRole(AttributeType attribute) {
- return deployment.getRoleAttributeNames().contains(attribute.getName()) || deployment.getRoleAttributeFriendlyNames().contains(attribute.getFriendlyName());
+ return (attribute.getName() != null && deployment.getRoleAttributeNames().contains(attribute.getName())) || (attribute.getFriendlyName() != null && deployment.getRoleAttributeNames().contains(attribute.getFriendlyName()));
}
protected AuthOutcome handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
index 4540b25..681e405 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -64,7 +64,6 @@ public interface SamlDeployment {
String getLogoutPage();
Set<String> getRoleAttributeNames();
- Set<String> getRoleAttributeFriendlyNames();
enum PrincipalNamePolicy {
FROM_NAME_ID,
diff --git a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
new file mode 100755
index 0000000..b9e0799
--- /dev/null
+++ b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xs:schema version="1.0"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns="urn:keycloak:saml:adapter"
+ targetNamespace="urn:keycloak:saml:adapter"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+
+ <xs:element name="keycloak-saml-adapter" type="adapter-type"/>
+ <xs:complexType name="adapter-type">
+ <xs:annotation>
+ <xs:documentation>
+ <![CDATA[
+ The Keycloak SAML Adapter keycloak-saml.xml config file
+ ]]>
+ </xs:documentation>
+ </xs:annotation>
+ <xs:all>
+ <xs:element name="SP" maxOccurs="1" minOccurs="0" type="sp-type"/>
+ </xs:all>
+ </xs:complexType>
+
+ <xs:complexType name="sp-type">
+ <xs:all>
+ <xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="PrincipalNameMapping" type="principal-name-mapping-type" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="RoleMapping" type="role-mapping-type" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="IDP" type="idp-type" minOccurs="1" maxOccurs="1"/>
+ </xs:all>
+ <xs:attribute name="entityID" type="xs:string" use="required"/>
+ <xs:attribute name="sslPolicy" type="xs:string" use="optional"/>
+ <xs:attribute name="nameIDPolicyFormat" type="xs:string" use="optional"/>
+ <xs:attribute name="logoutPage" type="xs:string" use="optional"/>
+ <xs:attribute name="forceAuthentication" type="xs:boolean" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="keys-type">
+ <xs:sequence>
+ <xs:element name="Key" type="key-type" minOccurs="1" maxOccurs="2"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="key-type">
+ <xs:all>
+ <xs:element name="KeyStore" maxOccurs="1" minOccurs="0" type="key-store-type"/>
+ <xs:element name="PrivateKeyPem" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="PublicKeyPem" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="CertificatePem" type="xs:string" minOccurs="0" maxOccurs="1"/>
+ </xs:all>
+ <xs:attribute name="signing" type="xs:boolean" use="optional"/>
+ <xs:attribute name="encryption" type="xs:boolean" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="key-store-type">
+ <xs:all>
+ <xs:element name="PrivateKey" maxOccurs="1" minOccurs="0" type="private-key-type"/>
+ <xs:element name="Certificate" type="certificate-type" minOccurs="0" maxOccurs="1"/>
+ </xs:all>
+ <xs:attribute name="file" type="xs:string" use="optional"/>
+ <xs:attribute name="resource" type="xs:string" use="optional"/>
+ <xs:attribute name="password" type="xs:string" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="private-key-type">
+ <xs:attribute name="alias" type="xs:string" use="required"/>
+ <xs:attribute name="password" type="xs:string" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="certificate-type">
+ <xs:attribute name="alias" type="xs:string" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="principal-name-mapping-type">
+ <xs:attribute name="policy" type="xs:string" use="required"/>
+ <xs:attribute name="attribute" type="xs:string" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="role-mapping-type">
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="Attribute" maxOccurs="unbounded" minOccurs="0" type="attribute-type"/>
+ </xs:choice>
+ </xs:complexType>
+ <xs:complexType name="attribute-type">
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="idp-type">
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="SingleSignOnService" maxOccurs="1" minOccurs="1" type="sign-on-type"/>
+ <xs:element name="SingleLogoutService" type="logout-type" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ <xs:attribute name="entityID" type="xs:string" use="required"/>
+ <xs:attribute name="signaturesRequired" type="xs:boolean" use="required"/>
+ <xs:attribute name="signatureAlgorithm" type="xs:string" use="optional"/>
+ <xs:attribute name="signatureCanonicalizationMethod" type="xs:string" use="optional"/>
+ <xs:attribute name="encryption" type="xs:boolean" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="sign-on-type">
+ <xs:attribute name="signRequest" type="xs:boolean" use="optional"/>
+ <xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional"/>
+ <xs:attribute name="requestBinding" type="xs:string" use="optional"/>
+ <xs:attribute name="responseBinding" type="xs:string" use="optional"/>
+ <xs:attribute name="bindingUrl" type="xs:string" use="optional"/>
+ </xs:complexType>
+
+ <xs:complexType name="logout-type">
+ <xs:attribute name="signRequest" type="xs:boolean" use="optional"/>
+ <xs:attribute name="signResponse" type="xs:boolean" use="optional"/>
+ <xs:attribute name="validateRequestSignature" type="xs:boolean" use="optional"/>
+ <xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional"/>
+ <xs:attribute name="requestBinding" type="xs:string" use="optional"/>
+ <xs:attribute name="responseBinding" type="xs:string" use="optional"/>
+ <xs:attribute name="postBindingUrl" type="xs:string" use="optional"/>
+ <xs:attribute name="redirectBindingUrl" type="xs:string" use="optional"/>
+ </xs:complexType>
+
+
+
+
+</xs:schema>
diff --git a/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
index c49cdf1..c92fec6 100755
--- a/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
+++ b/saml/client-adapter/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java
@@ -7,7 +7,15 @@ import org.keycloak.adapters.saml.config.Key;
import org.keycloak.adapters.saml.config.KeycloakSamlAdapter;
import org.keycloak.adapters.saml.config.SP;
import org.keycloak.adapters.saml.config.parsers.KeycloakSamlAdapterXMLParser;
+import org.keycloak.saml.common.util.StaxParserUtil;
+import javax.xml.XMLConstants;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
import java.io.InputStream;
/**
@@ -17,6 +25,37 @@ import java.io.InputStream;
public class XmlParserTest {
@Test
+ public void testValidation() throws Exception {
+ {
+ InputStream schema = KeycloakSamlAdapterXMLParser.class.getResourceAsStream("/schema/keycloak_saml_adapter_1_6.xsd");
+ InputStream is = getClass().getResourceAsStream("/keycloak-saml.xml");
+ Assert.assertNotNull(is);
+ Assert.assertNotNull(schema);
+ StaxParserUtil.validate(is, schema);
+ }
+ {
+ InputStream sch = KeycloakSamlAdapterXMLParser.class.getResourceAsStream("/schema/keycloak_saml_adapter_1_6.xsd");
+ InputStream doc = getClass().getResourceAsStream("/keycloak-saml2.xml");
+ Assert.assertNotNull(doc);
+ Assert.assertNotNull(sch);
+ try {
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ Schema schema = factory.newSchema(new StreamSource(sch));
+ Validator validator = schema.newValidator();
+ StreamSource source = new StreamSource(doc);
+ source.setSystemId("/keycloak-saml2.xml");
+ validator.validate(source);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+
+ }
+
+
+ }
+
+ @Test
public void testXmlParser() throws Exception {
InputStream is = getClass().getResourceAsStream("/keycloak-saml.xml");
Assert.assertNotNull(is);
@@ -48,11 +87,11 @@ public class XmlParserTest {
Assert.assertEquals("attribute", sp.getPrincipalNameMapping().getAttributeName());
Assert.assertTrue(sp.getRoleAttributes().size() == 1);
Assert.assertTrue(sp.getRoleAttributes().contains("member"));
- Assert.assertTrue(sp.getRoleFriendlyAttributes().size() == 1);
- Assert.assertTrue(sp.getRoleFriendlyAttributes().contains("memberOf"));
IDP idp = sp.getIdp();
Assert.assertEquals("idp", idp.getEntityID());
+ Assert.assertEquals("RSA", idp.getSignatureAlgorithm());
+ Assert.assertEquals("canon", idp.getSignatureCanonicalizationMethod());
Assert.assertTrue(idp.getSingleSignOnService().isSignRequest());
Assert.assertTrue(idp.getSingleSignOnService().isValidateResponseSignature());
Assert.assertEquals("post", idp.getSingleSignOnService().getRequestBinding());
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
index 242dc8e..5f88197 100755
--- a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
@@ -1,9 +1,7 @@
-<keycloak-saml-adapter>
+<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter">
<SP entityID="sp"
sslPolicy="ssl"
nameIDPolicyFormat="format"
- signatureAlgorithm=""
- sgnatureCanonicalizationMethod=""
forceAuthentication="true">
<Keys>
<Key signing="true" >
@@ -24,9 +22,12 @@
<PrincipalNameMapping policy="policy" attribute="attribute"/>
<RoleMapping>
<Attribute name="member"/>
- <FriendlyAttribute name="memberOf"/>
</RoleMapping>
- <IDP entityID="idp">
+ <IDP entityID="idp"
+ signatureAlgorithm="RSA"
+ signatureCanonicalizationMethod="canon"
+ signaturesRequired="true"
+ >
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="post"
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml2.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml2.xml
new file mode 100755
index 0000000..ee6388d
--- /dev/null
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml2.xml
@@ -0,0 +1,46 @@
+<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter">
+ <SP entityID="sp"
+ sslPolicy="ssl"
+ nameIDPolicyFormat="format"
+ signatureAlgorithm=""
+ signatureCanonicalizationMethod=""
+ forceAuthentication="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">
+ <PrivateKeyPemmm>
+ private pem
+ </PrivateKeyPemmm>
+ <PublicKeyPem>
+ public pem
+ </PublicKeyPem>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="policy" attribute="attribute"/>
+ <RoleMapping>
+ <Attribute name="member"/>
+ </RoleMapping>
+ <IDP entityID="idp"
+ signaturesRequired="true"
+ >
+ <SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="post"
+ bindingUrl="url"
+ />
+
+ <Keys>
+ <Key signing="true">
+ <CertificatePem>
+ cert pem
+ </CertificatePem>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml
new file mode 100755
index 0000000..8677d31
--- /dev/null
+++ b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Template used by WildFly build when directed to include Keycloak subsystem in a configuration. -->
+<config>
+ <extension-module>org.keycloak.keycloak-saml-adapter-subsystem</extension-module>
+ <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.6">
+ </subsystem>
+</config>
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
index 7941729..1fbec6f 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java
@@ -27,13 +27,16 @@ import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.ErrorCodes;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
@@ -41,7 +44,11 @@ import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
+import java.io.IOException;
import java.io.InputStream;
/**
@@ -54,7 +61,18 @@ public class StaxParserUtil {
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
- protected static Validator validator = null;
+ public static void validate(InputStream doc, InputStream sch) throws ParsingException {
+ try {
+ XMLEventReader xmlEventReader = StaxParserUtil.getXMLEventReader(doc);
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ Schema schema = factory.newSchema(new StreamSource(sch));
+ Validator validator = schema.newValidator();
+ validator.validate(new StAXSource(xmlEventReader));
+ } catch (Exception e) {
+ throw logger.parserException(e);
+ }
+
+ }
/**
* Bypass an entire XML element block from startElement to endElement
@@ -76,6 +94,29 @@ public class StaxParserUtil {
}
/**
+ * Advances reader if character whitespace encountered
+ *
+ * @param xmlEventReader
+ * @param xmlEvent
+ * @return
+ */
+ public static boolean wasWhitespacePeeked(XMLEventReader xmlEventReader, XMLEvent xmlEvent) {
+ if (xmlEvent.isCharacters()) {
+ Characters chars = xmlEvent.asCharacters();
+ String data = chars.getData();
+ if (data == null || data.trim().equals("")) {
+ try {
+ xmlEventReader.nextEvent();
+ return true;
+ } catch (XMLStreamException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Given an {@code Attribute}, get its trimmed value
*
* @param attribute
@@ -113,11 +154,23 @@ public class StaxParserUtil {
* @return false if attribute not set
*/
public static boolean getBooleanAttributeValue(StartElement startElement, String tag) {
+ return getBooleanAttributeValue(startElement, tag, false);
+ }
+
+ /**
+ * Get the Attribute value
+ *
+ * @param startElement
+ * @param tag localpart of the qname of the attribute
+ *
+ * @return false if attribute not set
+ */
+ public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) {
String result = null;
Attribute attr = startElement.getAttributeByName(new QName(tag));
if (attr != null)
result = getAttributeValue(attr);
- if (result == null) return false;
+ if (result == null) return defaultValue;
return Boolean.valueOf(result);
}
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index f5b05df..8460a13 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -16,18 +16,13 @@
<RoleMapping>
<Attribute name="Role"/>
</RoleMapping>
- <IDP entityID="idp">
- <SingleSignOnService signRequest="true"
- validateResponseSignature="true"
- requestBinding="POST"
+ <IDP entityID="idp"
+ signaturesRequired="true">
+ <SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
/>
<SingleLogoutService
- validateRequestSignature="true"
- validateResponseSignature="true"
- signRequest="true"
- signResponse="true"
requestBinding="POST"
responseBinding="POST"
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index 0a907ce..20d9348 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -16,18 +16,13 @@
<RoleMapping>
<Attribute name="Role"/>
</RoleMapping>
- <IDP entityID="idp">
- <SingleSignOnService signRequest="true"
- validateResponseSignature="true"
- requestBinding="POST"
- bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ <IDP entityID="idp"
+ signaturesRequired="true">
+ <SingleSignOnService requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
/>
<SingleLogoutService
- validateRequestSignature="true"
- validateResponseSignature="true"
- signRequest="true"
- signResponse="true"
requestBinding="POST"
responseBinding="POST"
postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"