[SAML] Handle ACS URL with existing query params (elastic/x-pack-elasticsearch#4060)
If the Assertion Consumer Service URL already contained query parameters, we would incorrectly append an addtional '?' rather than adding the SAML parameters to the end with '&' Original commit: elastic/x-pack-elasticsearch@60b6a977d8
This commit is contained in:
parent
c658238f33
commit
3a4fa16f03
|
@ -5,6 +5,13 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.security.authc.saml;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.opensaml.core.xml.util.XMLObjectSupport;
|
||||
import org.opensaml.saml.common.SAMLObject;
|
||||
import org.opensaml.saml.saml2.core.RequestAbstractType;
|
||||
import org.opensaml.saml.saml2.core.StatusResponseType;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureConstants;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
|
@ -13,13 +20,6 @@ import java.util.Base64;
|
|||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.opensaml.core.xml.util.XMLObjectSupport;
|
||||
import org.opensaml.saml.common.SAMLObject;
|
||||
import org.opensaml.saml.saml2.core.RequestAbstractType;
|
||||
import org.opensaml.saml.saml2.core.StatusResponseType;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureConstants;
|
||||
|
||||
public class SamlRedirect {
|
||||
|
||||
private final SAMLObject samlObject;
|
||||
|
@ -58,12 +58,22 @@ public class SamlRedirect {
|
|||
final byte[] sig = signing.sign(queryParam, algo);
|
||||
queryParam += "&Signature=" + urlEncode(base64Encode(sig));
|
||||
}
|
||||
return destination + "?" + queryParam;
|
||||
return withParameters(queryParam);
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Cannot construct SAML redirect", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String withParameters(String queryParam) {
|
||||
if (destination.indexOf('?') == -1) {
|
||||
return destination + "?" + queryParam;
|
||||
} else if (destination.endsWith("?")) {
|
||||
return destination + queryParam;
|
||||
} else {
|
||||
return destination + "&" + queryParam;
|
||||
}
|
||||
}
|
||||
|
||||
private String base64Encode(byte[] bytes) {
|
||||
return Base64.getEncoder().encodeToString(bytes);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.security.authc.saml;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.opensaml.saml.saml2.core.Issuer;
|
||||
import org.opensaml.saml.saml2.core.LogoutRequest;
|
||||
import org.opensaml.saml.saml2.core.NameID;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
public class SamlRedirectTests extends SamlTestCase {
|
||||
|
||||
private static final String IDP_ENTITY_ID = "https://idp.test/";
|
||||
private static final String LOGOUT_URL = "https://idp.test/saml/logout";
|
||||
|
||||
private static final SigningConfiguration NO_SIGNING = new SigningConfiguration(emptySet(), null);
|
||||
|
||||
public void testRedirectUrlWithoutRelayStateOrSigning() {
|
||||
final SamlRedirect redirect = new SamlRedirect(buildLogoutRequest(LOGOUT_URL), NO_SIGNING);
|
||||
final String url = redirect.getRedirectUrl();
|
||||
assertThat(url, equalTo(LOGOUT_URL + "?SAMLRequest=nZFBa4QwFIT%2FSnh3Naa2ax%2FqsiAFYdtDu91DLyVo2AY0cX2x9Oc36gpLCz30mAwz3" +
|
||||
"wwv2351LftUA2lrcohDDkyZ2jbanHJ4PTwEKWyLjGTXih739mRH96zOoyLHvNMQLlIO42DQStKERnaK0NX4snvcowg59oN1trYtsNIbtZFupn04" +
|
||||
"1xNGkW760HkhmrKidoYAq8oc3nUTi5vk9m6T3vsfolFVhpw0LgfB4zTgcRAnByEw2SDnIef8DdhxnePZcCmPs3m4Lv13Z0mkhqknFL96ZtF15kp" +
|
||||
"48hlV%2BS%2FCJAbL0sBP5StgiSwuzx8HKL4B"));
|
||||
}
|
||||
|
||||
public void testRedirectUrlWithRelayStateAndSigning() throws Exception {
|
||||
final SigningConfiguration signing = new SigningConfiguration(singleton("*"), buildOpenSamlCredential(createKeyPair()));
|
||||
final SamlRedirect redirect = new SamlRedirect(buildLogoutRequest(LOGOUT_URL), signing);
|
||||
final String url = redirect.getRedirectUrl("hello");
|
||||
assertThat(url, startsWith(LOGOUT_URL + "?SAMLRequest=nZFBa4QwFIT%2FSnh3Naa2ax%2FqsiAFYdtDu91DLyVo2AY0cX2x9Oc36gpLC" +
|
||||
"z30mAwz3wwv2351LftUA2lrcohDDkyZ2jbanHJ4PTwEKWyLjGTXih739mRH96zOoyLHvNMQLlIO42DQStKERnaK0NX4snvcowg59oN1trY" +
|
||||
"tsNIbtZFupn041xNGkW760HkhmrKidoYAq8oc3nUTi5vk9m6T3vsfolFVhpw0LgfB4zTgcRAnByEw2SDnIef8DdhxnePZcCmPs3m4Lv13Z" +
|
||||
"0mkhqknFL96ZtF15kp48hlV%2BS%2FCJAbL0sBP5StgiSwuzx8HKL4B" +
|
||||
"&RelayState=hello" +
|
||||
"&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256" +
|
||||
"&Signature="));
|
||||
}
|
||||
|
||||
public void testRedirectUrlWithExistingParameters() {
|
||||
final SamlRedirect redirect = new SamlRedirect(buildLogoutRequest(LOGOUT_URL + "?a=xyz"), NO_SIGNING);
|
||||
final String url = redirect.getRedirectUrl("foo");
|
||||
assertThat(url, equalTo(LOGOUT_URL + "?a=xyz" +
|
||||
"&SAMLRequest=nZFBS8QwFIT%2FSnn3tmmsbn00LUIRCqsHXT14kdCGNdAmtS%2BV1V9v2u7CouDBYzLMzDe8vDz0XfChRtLWCE" +
|
||||
"giBoEyjW212Qt42t2GGZRFTrLv%2BIBbu7eTe1DvkyIXeKchXCUB02jQStKERvaK0DX4eHO3RR4xHEbrbGM7CCpv1Ea6pe3NuYE" +
|
||||
"wjnU7RM4L8ZwVd0tJKcXh8wuCuhLwqtuEX6SXV5vs2v8QTao25KRxAjhLspAlYZLuOMd0g4xFjLEXCJ5PozwBHCfgYh7P0f8ml0" +
|
||||
"RqnGmh%2BEWbx%2BeZp4Z7n1FX%2F2qYxXBdGvqp7FSwRhbH548zFN8%3D" +
|
||||
"&RelayState=foo"));
|
||||
}
|
||||
|
||||
public void testRedirectUrlWithTrailingQuestionMark() {
|
||||
final SamlRedirect redirect = new SamlRedirect(buildLogoutRequest(LOGOUT_URL + "?"), NO_SIGNING);
|
||||
final String url = redirect.getRedirectUrl();
|
||||
assertThat(url, equalTo(LOGOUT_URL + "?SAMLRequest=nZFPS8QwFMS%2FSnj3tmmsbn30D0IRCqsHXffgRUIb1kCb1L5U%2FPim7R" +
|
||||
"YWBQ8ek2HmN8PLyq%2B%2BY59qJG1NDnHIgSnT2FabUw4vh%2FsghbLISPadGHBvT3ZyT%2BpjUuSYdxrCVcphGg1aSZrQyF4Rug" +
|
||||
"af7x72KEKOw2idbWwHrPJGbaRbaO%2FODYRRpNshdF6I5qyoWyAlsLrK4U23sbhKrm926a3%2FIZpUbchJ43IQPE4DHgdxchACkx" +
|
||||
"1yHnLOX4Edtz0eDuf2uJjHy9Z%2Fl5ZEapyLQvGraBZdZm6ER59RV%2F8izGKwLg38VL4B1sji%2FPxxgeIb"));
|
||||
}
|
||||
|
||||
private LogoutRequest buildLogoutRequest(String logoutUrl) {
|
||||
final LogoutRequest logoutRequest = SamlUtils.buildObject(LogoutRequest.class, LogoutRequest.DEFAULT_ELEMENT_NAME);
|
||||
logoutRequest.setDestination(logoutUrl);
|
||||
logoutRequest.setIssueInstant(new DateTime(2018, 1, 14, 22, 47, DateTimeZone.UTC));
|
||||
logoutRequest.setID("_id123456789");
|
||||
final Issuer issuer = SamlUtils.buildObject(Issuer.class, Issuer.DEFAULT_ELEMENT_NAME);
|
||||
issuer.setValue(IDP_ENTITY_ID);
|
||||
logoutRequest.setIssuer(issuer);
|
||||
final NameID nameId = SamlUtils.buildObject(NameID.class, NameID.DEFAULT_ELEMENT_NAME);
|
||||
nameId.setValue("name-123456-7890");
|
||||
logoutRequest.setNameID(nameId);
|
||||
return logoutRequest;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue