Skip to content

Commit

Permalink
KEYCLOAK-14741 Minor SAML specs compliance improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
lscorcia authored and hmlnarik committed Jul 20, 2020
1 parent 93149d6 commit 46bf139
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
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.NAMEID_FORMAT_TRANSIENT;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;

/**
Expand Down Expand Up @@ -89,7 +90,10 @@ public void write(AuthnRequestType request) throws ProcessingException {
}

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

Expand Down Expand Up @@ -223,7 +227,8 @@ public void write(NameIDPolicyType nameIDPolicy) throws ProcessingException {
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ParsingException;
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.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
Expand Down Expand Up @@ -39,11 +40,18 @@
import org.apache.http.util.EntityUtils;
import org.hamcrest.Matcher;
import org.jboss.resteasy.util.Encode;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import static org.hamcrest.CoreMatchers.not;
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.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.utils.io.IOUtil.documentToString;
import static org.keycloak.testsuite.utils.io.IOUtil.setDocElementAttributeValue;
Expand Down Expand Up @@ -241,4 +249,62 @@ private void testReauthnWithForceAuthn(Boolean reloginRequired) throws Exception

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

0 comments on commit 46bf139

Please sign in to comment.