saml adapter schema and simplifications
This commit is contained in:
parent
8acdaf462b
commit
d06237a3fd
18 changed files with 318 additions and 92 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -64,7 +64,6 @@ public interface SamlDeployment {
|
|||
String getLogoutPage();
|
||||
|
||||
Set<String> getRoleAttributeNames();
|
||||
Set<String> getRoleAttributeFriendlyNames();
|
||||
|
||||
enum PrincipalNamePolicy {
|
||||
FROM_NAME_ID,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
115
saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
Executable file
115
saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
Executable file
|
@ -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>
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -16,6 +24,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");
|
||||
|
@ -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());
|
||||
|
|
|
@ -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"
|
||||
|
|
46
saml/client-adapter/core/src/test/resources/keycloak-saml2.xml
Executable file
46
saml/client-adapter/core/src/test/resources/keycloak-saml2.xml
Executable file
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
|
@ -75,6 +93,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
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue