KEYCLOAK-14741 Minor SAML specs compliance improvements
This commit is contained in:
parent
93149d6b47
commit
46bf139cb4
2 changed files with 73 additions and 2 deletions
|
@ -39,6 +39,7 @@ import java.util.List;
|
||||||
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
|
|
||||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
||||||
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT;
|
||||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,7 +90,10 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean isPassive = request.isIsPassive();
|
Boolean isPassive = request.isIsPassive();
|
||||||
if (isPassive != null) {
|
// The AuthnRequest IsPassive attribute is optional and if omitted its default value is false.
|
||||||
|
// Some IdPs refuse requests if the IsPassive attribute is present and set to false, so to
|
||||||
|
// maximize compatibility we emit it only if it is set to true
|
||||||
|
if (isPassive != null && isPassive == true) {
|
||||||
StaxUtil.writeAttribute(writer, JBossSAMLConstants.IS_PASSIVE.get(), isPassive.toString());
|
StaxUtil.writeAttribute(writer, JBossSAMLConstants.IS_PASSIVE.get(), isPassive.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +227,8 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean allowCreate = nameIDPolicy.isAllowCreate();
|
Boolean allowCreate = nameIDPolicy.isAllowCreate();
|
||||||
if (allowCreate != null) {
|
// The NameID AllowCreate attribute must not be used when using the transient NameID format.
|
||||||
|
if (allowCreate != null && (format == null || !NAMEID_FORMAT_TRANSIENT.get().equals(format.toASCIIString()))) {
|
||||||
StaxUtil.writeAttribute(writer, JBossSAMLConstants.ALLOW_CREATE.get(), allowCreate.toString());
|
StaxUtil.writeAttribute(writer, JBossSAMLConstants.ALLOW_CREATE.get(), allowCreate.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||||
|
import org.keycloak.saml.common.util.DocumentUtil;
|
||||||
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||||
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
|
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
|
||||||
|
@ -39,11 +40,18 @@ import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
import org.hamcrest.Matcher;
|
import org.hamcrest.Matcher;
|
||||||
import org.jboss.resteasy.util.Encode;
|
import org.jboss.resteasy.util.Encode;
|
||||||
|
import org.w3c.dom.Attr;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.not;
|
import static org.hamcrest.CoreMatchers.not;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT;
|
||||||
|
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
|
||||||
import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_PORT;
|
import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_PORT;
|
||||||
import static org.keycloak.testsuite.utils.io.IOUtil.documentToString;
|
import static org.keycloak.testsuite.utils.io.IOUtil.documentToString;
|
||||||
import static org.keycloak.testsuite.utils.io.IOUtil.setDocElementAttributeValue;
|
import static org.keycloak.testsuite.utils.io.IOUtil.setDocElementAttributeValue;
|
||||||
|
@ -241,4 +249,62 @@ public class BasicSamlTest extends AbstractSamlTest {
|
||||||
|
|
||||||
samlClient.execute(secondAuthn);
|
samlClient.execute(secondAuthn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsPassiveAttributeEmittedWhenTrue() throws Exception {
|
||||||
|
// Verifies that the IsPassive attribute is emitted in the authnRequest
|
||||||
|
// when it is set to true
|
||||||
|
|
||||||
|
// Build the login request document
|
||||||
|
AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, getAuthServerSamlEndpoint(REALM_NAME));
|
||||||
|
loginRep.setIsPassive(true);
|
||||||
|
|
||||||
|
Document document = SAML2Request.convert(loginRep);
|
||||||
|
|
||||||
|
// Find the AuthnRequest element
|
||||||
|
Element authnRequestElement = document.getDocumentElement();
|
||||||
|
Attr isPassiveAttribute = authnRequestElement.getAttributeNode("IsPassive");
|
||||||
|
assertThat("AuthnRequest element should contain the IsPassive attribute when isPassive is true, but it doesn't", isPassiveAttribute, notNullValue());
|
||||||
|
assertThat("AuthnRequest/IsPassive attribute should be true when isPassive is true, but it isn't", isPassiveAttribute.getNodeValue(), is("true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsPassiveAttributeOmittedWhenFalse() throws Exception {
|
||||||
|
// Verifies that the IsPassive attribute is not emitted in the authnRequest
|
||||||
|
// when it is set to false
|
||||||
|
|
||||||
|
// Build the login request document
|
||||||
|
AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, getAuthServerSamlEndpoint(REALM_NAME));
|
||||||
|
loginRep.setIsPassive(false);
|
||||||
|
|
||||||
|
Document document = SAML2Request.convert(loginRep);
|
||||||
|
|
||||||
|
// Find the AuthnRequest element
|
||||||
|
Element authnRequestElement = document.getDocumentElement();
|
||||||
|
Attr isPassiveAttribute = authnRequestElement.getAttributeNode("IsPassive");
|
||||||
|
assertThat("AuthnRequest element shouldn't contain the IsPassive attribute when isPassive is false, but it does", isPassiveAttribute, nullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllowCreateAttributeOmittedWhenTransient() throws Exception {
|
||||||
|
// Verifies that the AllowCreate attribute is not emitted in the AuthnRequest
|
||||||
|
// when NameIDFormat is Transient
|
||||||
|
|
||||||
|
// Build the login request document
|
||||||
|
AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, getAuthServerSamlEndpoint(REALM_NAME));
|
||||||
|
loginRep.getNameIDPolicy().setFormat(NAMEID_FORMAT_TRANSIENT.getUri());
|
||||||
|
loginRep.getNameIDPolicy().setAllowCreate(true);
|
||||||
|
|
||||||
|
Document document = SAML2Request.convert(loginRep);
|
||||||
|
|
||||||
|
// Find the AuthnRequest element
|
||||||
|
Element authnRequestElement = document.getDocumentElement();
|
||||||
|
Element nameIdPolicyElement = DocumentUtil.getDirectChildElement(authnRequestElement, PROTOCOL_NSURI.get(), "NameIDPolicy");
|
||||||
|
|
||||||
|
Attr formatAttribute = nameIdPolicyElement.getAttributeNode("Format");
|
||||||
|
Attr allowCreateAttribute = nameIdPolicyElement.getAttributeNode("AllowCreate");
|
||||||
|
assertThat("AuthnRequest/NameIdPolicy Format should be present, but it is not", formatAttribute, notNullValue());
|
||||||
|
assertThat("AuthnRequest/NameIdPolicy Format should be Transient, but it is not", formatAttribute.getNodeValue(), is(NAMEID_FORMAT_TRANSIENT.get()));
|
||||||
|
assertThat("AuthnRequest/NameIdPolicy element shouldn't contain the AllowCreate attribute when Format is set to Transient, but it does", allowCreateAttribute, nullValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue