mirror of
https://github.com/keycloak/keycloak.git
synced 2026-02-18 18:37:54 -05:00
Limit the inflating size for the SAML redirect binding
Closes #46372 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
4253a79eb2
commit
4f90ef67f6
11 changed files with 230 additions and 55 deletions
|
|
@ -76,6 +76,7 @@ import org.keycloak.saml.common.exceptions.ConfigurationException;
|
|||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
||||
import org.keycloak.saml.processing.api.util.DeflateUtil;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
|
|
@ -100,6 +101,8 @@ import static org.keycloak.adapters.saml.SamlPrincipal.DEFAULT_ROLE_ATTRIBUTE_NA
|
|||
*/
|
||||
public abstract class AbstractSamlAuthenticationHandler implements SamlAuthenticationHandler {
|
||||
|
||||
public static final String MAX_INFLAFING_SIZE_PROP = "org.keycloak.adapters.saml.maxInflatingSize";
|
||||
private static final long MAX_INFLAFING_SIZE = Long.getLong(MAX_INFLAFING_SIZE_PROP, DeflateUtil.DEFAULT_MAX_INFLATING_SIZE);
|
||||
protected static Logger log = Logger.getLogger(WebBrowserSsoAuthenticationHandler.class);
|
||||
|
||||
protected final HttpFacade facade;
|
||||
|
|
@ -177,7 +180,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
|||
if (index > -1) {
|
||||
requestUri = requestUri.substring(0, index);
|
||||
}
|
||||
holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
||||
holder = SAMLRequestParser.parseRequestRedirectBinding(samlRequest, MAX_INFLAFING_SIZE);
|
||||
} else {
|
||||
postBinding = true;
|
||||
holder = SAMLRequestParser.parseRequestPostBinding(samlRequest);
|
||||
|
|
@ -634,7 +637,7 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
|||
}
|
||||
|
||||
protected SAMLDocumentHolder extractRedirectBindingResponse(String response) {
|
||||
return SAMLRequestParser.parseRequestRedirectBinding(response);
|
||||
return SAMLRequestParser.parseRequestRedirectBinding(response, MAX_INFLAFING_SIZE);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@
|
|||
:upgradingguide_link: {project_doc_base_url}/upgrading/
|
||||
:upgradingguide_link_latest: {project_doc_base_url_latest}/upgrading/
|
||||
:upgradingclientlibs_link: https://www.keycloak.org/securing-apps/upgrading
|
||||
:saml_galleon_layers_link: https://www.keycloak.org/securing-apps/saml-galleon-layers
|
||||
:upgradingclientlibs_name: Upgrading {project_name} Client libraries
|
||||
:releasenotes_name: Release Notes
|
||||
:releasenotes_name_short: {releasenotes_name}
|
||||
|
|
|
|||
|
|
@ -21,3 +21,15 @@ It also lists significant changes to internal APIs.
|
|||
In version 26.4.0, the `server-info` endpoint changed to just return the system information for administrators in the admin realm. Nevertheless, the version property was detected to be needed by some products that interact with {project_name}. Now that property is included for administrators in the realm with permission `manage-realm`.
|
||||
|
||||
The workaround of the `view-system` permission is more restricted too. It can only be assigned by administrators in the master realm using link:{adminguide_link}#_fine_grained_permissions[FGAP]. This permission will be deleted in a future version.
|
||||
|
||||
=== Maximum inflating size for the SAML redirect binding
|
||||
|
||||
Since this release, the {project_name} SAML implementation limits the data that can be inflated through the `REDIRECT` binding. The default maximum size is 128KB, the decompression stops when that value is exceeded and returns an error. The option `spi-login-protocol--saml--max-inflating-size` can be used to increase the default limit.
|
||||
|
||||
.Increasing limit to 512KB
|
||||
[source,bash]
|
||||
----
|
||||
bin/kc.[sh|bat] --spi-login-protocol--saml--max-inflating-size=524288
|
||||
----
|
||||
|
||||
The same restriction is applied for the link:{saml_galleon_layers_link}[{project_name} SAML Galleon feature pack]. Although, in this case, you need to add a system property to the Wildfly/EAP server to change the default maximum size: `-Dorg.keycloak.adapters.saml.maxInflatingSize=524288`.
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
package org.keycloak.saml;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.keycloak.common.util.StreamUtil;
|
||||
|
|
@ -27,6 +26,7 @@ import org.keycloak.saml.common.PicketLinkLoggerFactory;
|
|||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
||||
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
|
||||
import org.keycloak.saml.processing.api.util.DeflateUtil;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.saml.processing.web.util.PostBindingUtil;
|
||||
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
|
||||
|
|
@ -42,26 +42,17 @@ public class SAMLRequestParser {
|
|||
protected static Logger log = Logger.getLogger(SAMLRequestParser.class);
|
||||
|
||||
public static SAMLDocumentHolder parseRequestRedirectBinding(String samlMessage) {
|
||||
InputStream is;
|
||||
try {
|
||||
is = RedirectBindingUtil.base64DeflateDecode(samlMessage);
|
||||
} catch (IOException e) {
|
||||
logger.samlBase64DecodingError(e);
|
||||
return null;
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
String message = null;
|
||||
try {
|
||||
message = StreamUtil.readString(is, GeneralConstants.SAML_CHARSET);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
log.debug("SAML Redirect Binding");
|
||||
log.debug(message);
|
||||
is = new ByteArrayInputStream(message.getBytes(GeneralConstants.SAML_CHARSET));
|
||||
return parseRequestRedirectBinding(samlMessage, DeflateUtil.DEFAULT_MAX_INFLATING_SIZE);
|
||||
}
|
||||
|
||||
}
|
||||
try {
|
||||
public static SAMLDocumentHolder parseRequestRedirectBinding(String samlMessage, long maxInflatingSize) {
|
||||
try (InputStream is = RedirectBindingUtil.base64DeflateDecode(samlMessage, maxInflatingSize)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
String message = StreamUtil.readString(is, GeneralConstants.SAML_CHARSET);
|
||||
log.debug("SAML Redirect Binding");
|
||||
log.debug(message);
|
||||
return SAML2Request.getSAML2ObjectFromStream(new ByteArrayInputStream(message.getBytes(GeneralConstants.SAML_CHARSET)));
|
||||
}
|
||||
return SAML2Request.getSAML2ObjectFromStream(is);
|
||||
} catch (Exception e) {
|
||||
logger.samlBase64DecodingError(e);
|
||||
|
|
@ -110,27 +101,20 @@ public class SAMLRequestParser {
|
|||
}
|
||||
|
||||
public static SAMLDocumentHolder parseResponseRedirectBinding(String samlMessage) {
|
||||
InputStream is;
|
||||
try {
|
||||
is = RedirectBindingUtil.base64DeflateDecode(samlMessage);
|
||||
} catch (IOException e) {
|
||||
logger.samlBase64DecodingError(e);
|
||||
return null;
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
String message = null;
|
||||
try {
|
||||
message = StreamUtil.readString(is, GeneralConstants.SAML_CHARSET);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
log.debug("SAML Redirect Binding");
|
||||
log.debug(message);
|
||||
is = new ByteArrayInputStream(message.getBytes(GeneralConstants.SAML_CHARSET));
|
||||
return parseResponseRedirectBinding(samlMessage, DeflateUtil.DEFAULT_MAX_INFLATING_SIZE);
|
||||
}
|
||||
|
||||
}
|
||||
SAML2Response response = new SAML2Response();
|
||||
try {
|
||||
public static SAMLDocumentHolder parseResponseRedirectBinding(String samlMessage, long maxInflatingSize) {
|
||||
try (InputStream is = RedirectBindingUtil.base64DeflateDecode(samlMessage, maxInflatingSize)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
String message = StreamUtil.readString(is, GeneralConstants.SAML_CHARSET);
|
||||
log.debug("SAML Redirect Binding");
|
||||
log.debug(message);
|
||||
SAML2Response response = new SAML2Response();
|
||||
response.getSAML2ObjectFromStream(new ByteArrayInputStream(message.getBytes(GeneralConstants.SAML_CHARSET)));
|
||||
return response.getSamlDocumentHolder();
|
||||
}
|
||||
SAML2Response response = new SAML2Response();
|
||||
response.getSAML2ObjectFromStream(is);
|
||||
return response.getSamlDocumentHolder();
|
||||
} catch (Exception e) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,15 @@ import org.keycloak.saml.common.constants.GeneralConstants;
|
|||
*/
|
||||
public class DeflateUtil {
|
||||
|
||||
/**
|
||||
* Maximum size for inflating. Default is 128KB like quarkus.http.limits.max-form-attribute-size.
|
||||
*/
|
||||
public static long DEFAULT_MAX_INFLATING_SIZE = 131072;
|
||||
|
||||
private DeflateUtil() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply DEFLATE encoding
|
||||
*
|
||||
|
|
@ -75,7 +84,90 @@ public class DeflateUtil {
|
|||
* @return
|
||||
*/
|
||||
public static InputStream decode(byte[] msgToDecode) {
|
||||
return decode(msgToDecode, DEFAULT_MAX_INFLATING_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* DEFLATE decoding
|
||||
*
|
||||
* @param msgToDecode the message that needs decoding
|
||||
* @param maxInflatingSize the maximum size to inflate, IOExceptio is thrown if more data is inflated
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static InputStream decode(byte[] msgToDecode, long maxInflatingSize) {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(msgToDecode);
|
||||
return new InflaterInputStream(bais, new Inflater(true));
|
||||
return new LimitedInflaterInputStream(bais, maxInflatingSize);
|
||||
}
|
||||
|
||||
private static class LimitedInflaterInputStream extends InputStream {
|
||||
|
||||
private final InflaterInputStream is;
|
||||
private final Inflater inflater;
|
||||
private final long maxInflatingSize;
|
||||
|
||||
private LimitedInflaterInputStream(InputStream is, long maxInflatingSize) {
|
||||
this.inflater = new Inflater(true);
|
||||
this.is = new InflaterInputStream(is, inflater);
|
||||
this.maxInflatingSize = maxInflatingSize;
|
||||
}
|
||||
|
||||
private void checkMaxInflatingsize() throws IOException {
|
||||
if (inflater.getBytesWritten() > maxInflatingSize) {
|
||||
throw new IOException(String.format("Maximum inflating size %d reached. Total bytes witten %d.",
|
||||
maxInflatingSize, inflater.getTotalOut()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
int result = is.read();
|
||||
checkMaxInflatingsize();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
int result = is.read(b, off, len);
|
||||
checkMaxInflatingsize();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
int result = is.read(b);
|
||||
checkMaxInflatingsize();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
throw new IOException("mark/reset not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(int readlimit) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
is.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return is.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
return is.skip(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -152,8 +152,22 @@ public class RedirectBindingUtil {
|
|||
* @throws IOException
|
||||
*/
|
||||
public static InputStream urlBase64DeflateDecode(String encodedString) throws IOException {
|
||||
return urlBase64DeflateDecode(encodedString, DeflateUtil.DEFAULT_MAX_INFLATING_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply URL decoding, followed by base64 decoding followed by deflate decompression
|
||||
*
|
||||
* @param encodedString
|
||||
* @param maxInflatingSize
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static InputStream urlBase64DeflateDecode(String encodedString, long maxInflatingSize) throws IOException {
|
||||
byte[] deflatedString = urlBase64Decode(encodedString);
|
||||
return DeflateUtil.decode(deflatedString);
|
||||
return DeflateUtil.decode(deflatedString, maxInflatingSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -166,8 +180,22 @@ public class RedirectBindingUtil {
|
|||
* @throws IOException
|
||||
*/
|
||||
public static InputStream base64DeflateDecode(String encodedString) throws IOException {
|
||||
return base64DeflateDecode(encodedString, DeflateUtil.DEFAULT_MAX_INFLATING_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 decode followed by Deflate decoding
|
||||
*
|
||||
* @param encodedString
|
||||
* @param maxInflatingSize
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public static InputStream base64DeflateDecode(String encodedString, long maxInflatingSize) throws IOException {
|
||||
byte[] base64decodedMsg = Base64.getMimeDecoder().decode(encodedString);
|
||||
return DeflateUtil.decode(base64decodedMsg);
|
||||
return DeflateUtil.decode(base64decodedMsg, maxInflatingSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ import org.keycloak.protocol.saml.SamlMetadataKeyLocator;
|
|||
import org.keycloak.protocol.saml.SamlMetadataPublicKeyLoader;
|
||||
import org.keycloak.protocol.saml.SamlPrincipalType;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.keycloak.protocol.saml.SamlProtocolFactory;
|
||||
import org.keycloak.protocol.saml.SamlProtocolUtils;
|
||||
import org.keycloak.protocol.saml.SamlService;
|
||||
import org.keycloak.protocol.saml.SamlSessionUtils;
|
||||
|
|
@ -154,6 +155,7 @@ public class SAMLEndpoint {
|
|||
protected final KeycloakSession session;
|
||||
protected final ClientConnection clientConnection;
|
||||
protected final HttpHeaders headers;
|
||||
protected final long maxInflatingSize;
|
||||
|
||||
|
||||
public SAMLEndpoint(KeycloakSession session, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, UserAuthenticationIdentityProvider.AuthenticationCallback callback, DestinationValidator destinationValidator) {
|
||||
|
|
@ -165,6 +167,8 @@ public class SAMLEndpoint {
|
|||
this.session = session;
|
||||
this.clientConnection = session.getContext().getConnection();
|
||||
this.headers = session.getContext().getRequestHeaders();
|
||||
SamlProtocolFactory factory = (SamlProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, SamlProtocol.LOGIN_PROTOCOL);
|
||||
this.maxInflatingSize = factory.getMaxInflatingSize();
|
||||
}
|
||||
|
||||
@GET
|
||||
|
|
@ -298,6 +302,12 @@ public class SAMLEndpoint {
|
|||
|
||||
protected Response handleSamlRequest(String samlRequest, String relayState) {
|
||||
SAMLDocumentHolder holder = extractRequestDocument(samlRequest);
|
||||
if (holder == null) {
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.detail(Details.REASON, Errors.INVALID_SAML_DOCUMENT);
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);
|
||||
}
|
||||
RequestAbstractType requestAbstractType = (RequestAbstractType) holder.getSamlObject();
|
||||
// validate destination
|
||||
if (isDestinationRequired() &&
|
||||
|
|
@ -878,12 +888,12 @@ public class SAMLEndpoint {
|
|||
|
||||
@Override
|
||||
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
|
||||
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
||||
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest, maxInflatingSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SAMLDocumentHolder extractResponseDocument(String response) {
|
||||
return SAMLRequestParser.parseResponseRedirectBinding(response);
|
||||
return SAMLRequestParser.parseResponseRedirectBinding(response, maxInflatingSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -40,10 +40,13 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
|||
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
|
||||
import org.keycloak.protocol.saml.mappers.RoleListMapper;
|
||||
import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.representations.idm.CertificateRepresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.saml.SignatureAlgorithm;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.processing.api.util.DeflateUtil;
|
||||
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
|
||||
import org.keycloak.saml.validators.DestinationValidator;
|
||||
|
||||
|
|
@ -57,10 +60,11 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
private static final String ROLE_LIST_CONSENT_TEXT = "${samlRoleListScopeConsentText}";
|
||||
|
||||
private DestinationValidator destinationValidator;
|
||||
private long maxInflatingSize;
|
||||
|
||||
@Override
|
||||
public Object createProtocolEndpoint(KeycloakSession session, EventBuilder event) {
|
||||
return new SamlService(session, event, destinationValidator);
|
||||
return new SamlService(session, event, maxInflatingSize, destinationValidator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -103,6 +107,7 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
defaultBuiltins.add(model);
|
||||
}
|
||||
this.destinationValidator = DestinationValidator.forProtocolMap(config.getArray("knownProtocols"));
|
||||
this.maxInflatingSize = config.getLong("maxInflatingSize", DeflateUtil.DEFAULT_MAX_INFLATING_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -202,4 +207,24 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
public int order() {
|
||||
return OIDCLoginProtocolFactory.UI_ORDER - 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the max inflating size
|
||||
* @return
|
||||
*/
|
||||
public long getMaxInflatingSize() {
|
||||
return maxInflatingSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigMetadata() {
|
||||
return ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name("maxInflatingSize")
|
||||
.type("long")
|
||||
.helpText("The maximum inflating size in bytes for the REDIRECT binding.")
|
||||
.defaultValue(DeflateUtil.DEFAULT_MAX_INFLATING_SIZE)
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,10 +159,12 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
public static final String ARTIFACT_RESOLUTION_SERVICE_PATH = "resolve";
|
||||
|
||||
private final DestinationValidator destinationValidator;
|
||||
private final long maxInflatingSize;
|
||||
|
||||
public SamlService(KeycloakSession session, EventBuilder event, DestinationValidator destinationValidator) {
|
||||
public SamlService(KeycloakSession session, EventBuilder event, long maxInflatingSize, DestinationValidator destinationValidator) {
|
||||
super(session, event);
|
||||
this.destinationValidator = destinationValidator;
|
||||
this.maxInflatingSize = maxInflatingSize;
|
||||
}
|
||||
|
||||
public abstract class BindingProtocol {
|
||||
|
|
@ -204,7 +206,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
event.event(EventType.LOGOUT);
|
||||
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
|
||||
|
||||
if (! (holder.getSamlObject() instanceof StatusResponseType)) {
|
||||
if (holder == null || !(holder.getSamlObject() instanceof StatusResponseType)) {
|
||||
event.detail(Details.REASON, Errors.INVALID_SAML_RESPONSE);
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
return error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_REQUEST);
|
||||
|
|
@ -848,12 +850,12 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
|
||||
@Override
|
||||
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
|
||||
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
||||
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest, maxInflatingSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SAMLDocumentHolder extractResponseDocument(String response) {
|
||||
return SAMLRequestParser.parseResponseRedirectBinding(response);
|
||||
return SAMLRequestParser.parseResponseRedirectBinding(response, maxInflatingSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1120,7 +1122,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
@NoCache
|
||||
@Consumes({"application/soap+xml",MediaType.TEXT_XML})
|
||||
public Response soapBinding(InputStream inputStream) {
|
||||
SamlEcpProfileService bindingService = new SamlEcpProfileService(session, event, destinationValidator);
|
||||
SamlEcpProfileService bindingService = new SamlEcpProfileService(session, event, maxInflatingSize, destinationValidator);
|
||||
|
||||
return bindingService.authenticate(inputStream);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ public class SamlEcpProfileService extends SamlService {
|
|||
private static final String NS_PREFIX_SAML_PROTOCOL = "samlp";
|
||||
private static final String NS_PREFIX_SAML_ASSERTION = "saml";
|
||||
|
||||
public SamlEcpProfileService(KeycloakSession session, EventBuilder event, DestinationValidator destinationValidator) {
|
||||
super(session, event, destinationValidator);
|
||||
public SamlEcpProfileService(KeycloakSession session, EventBuilder event, long maxInflatingSize, DestinationValidator destinationValidator) {
|
||||
super(session, event, maxInflatingSize, destinationValidator);
|
||||
}
|
||||
|
||||
public Response authenticate(InputStream inputStream) {
|
||||
|
|
|
|||
|
|
@ -17,12 +17,14 @@
|
|||
|
||||
package org.keycloak.test.broker.saml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
import org.keycloak.saml.SAMLRequestParser;
|
||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.saml.processing.web.util.PostBindingUtil;
|
||||
import org.keycloak.saml.processing.web.util.RedirectBindingUtil;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
|
@ -35,6 +37,7 @@ import org.junit.Test;
|
|||
public class SAMLParsingTest {
|
||||
|
||||
private static final String SAML_RESPONSE = "<samlp:LogoutResponse xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" Destination=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\" ID=\"ID_9a171d23-c417-42f5-9bca-c093123fd68c\" InResponseTo=\"ID_bc730711-2037-43f3-ad76-7bc33842fb87\" IssueInstant=\"2016-02-29T12:00:14.044Z\" Version=\"2.0\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status></samlp:LogoutResponse>";
|
||||
private static final String SAML_REQUEST = "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" AssertionConsumerServiceURL=\"http://localhost:8080/realms/master/broker/saml/endpoint\" AttributeConsumingServiceIndex=\"0\" Destination=\"http://localhost:8080/realms/saml/protocol/saml\" ForceAuthn=\"false\" ID=\"ID_7228aef5-4a58-4481-a371-30e4ad7e98f4\" IssueInstant=\"2026-02-16T11:23:32.472Z\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Version=\"2.0\"><saml:Issuer>http://localhost:8080/realms/master</saml:Issuer><samlp:NameIDPolicy AllowCreate=\"true\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\"/></samlp:AuthnRequest>";
|
||||
|
||||
@Test
|
||||
public void parseTest() {
|
||||
|
|
@ -51,4 +54,19 @@ public class SAMLParsingTest {
|
|||
SAMLDocumentHolder holder = SAMLRequestParser.parseResponseDocument(samlBytes);
|
||||
Assert.assertNotNull(holder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseRequestResponseRedirectBinding() throws IOException {
|
||||
String encodedResponse = RedirectBindingUtil.deflateBase64Encode(SAML_RESPONSE.getBytes(GeneralConstants.SAML_CHARSET_NAME));
|
||||
SAMLDocumentHolder holder = SAMLRequestParser.parseResponseRedirectBinding(encodedResponse, SAML_RESPONSE.length());
|
||||
Assert.assertNotNull(holder);
|
||||
holder = SAMLRequestParser.parseResponseRedirectBinding(encodedResponse, SAML_RESPONSE.length() - 1);
|
||||
Assert.assertNull(holder);
|
||||
|
||||
String encodedRequest = RedirectBindingUtil.deflateBase64Encode(SAML_REQUEST.getBytes(GeneralConstants.SAML_CHARSET_NAME));
|
||||
holder = SAMLRequestParser.parseRequestRedirectBinding(encodedRequest, SAML_REQUEST.length());
|
||||
Assert.assertNotNull(holder);
|
||||
holder = SAMLRequestParser.parseRequestRedirectBinding(encodedRequest, SAML_RESPONSE.length() - 1);
|
||||
Assert.assertNull(holder);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue