package org.keycloak.protocol.cas.utils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.keycloak.connections.httpclient.HttpClientProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator; import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import javax.ws.rs.core.HttpHeaders; import javax.xml.datatype.XMLGregorianCalendar; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; public class LogoutHelper { //although it looks alike, the CAS SLO protocol has nothing to do with SAML; so we build the format //required by the spec manually private static final String TEMPLATE = "\n" + " @NOT_USED@\n" + " $SESSION_IDENTIFIER\n" + ""; public static HttpEntity buildSingleLogoutRequest(String serviceTicket) { String id = IDGenerator.create("ID_"); XMLGregorianCalendar issueInstant; try { issueInstant = XMLTimeUtil.getIssueInstant(); } catch (ConfigurationException e) { throw new RuntimeException(e); } String document = TEMPLATE.replace("$ID", id).replace("$ISSUE_INSTANT", issueInstant.toString()) .replace("$SESSION_IDENTIFIER", serviceTicket); return new StringEntity(document, ContentType.APPLICATION_XML.withCharset(StandardCharsets.UTF_8)); } public static void postWithRedirect(KeycloakSession session, String url, HttpEntity postBody) throws IOException { HttpClient httpClient = session.getProvider(HttpClientProvider.class).getHttpClient(); for (int i = 0; i < 2; i++) { // follow redirects once HttpPost post = new HttpPost(url); post.setEntity(postBody); HttpResponse response = httpClient.execute(post); try { int status = response.getStatusLine().getStatusCode(); if (status == 302 && !url.endsWith("/")) { String redirect = response.getFirstHeader(HttpHeaders.LOCATION).getValue(); String withSlash = url + "/"; if (withSlash.equals(redirect)) { url = withSlash; continue; } } } finally { HttpEntity entity = response.getEntity(); if (entity != null) { InputStream is = entity.getContent(); if (is != null) is.close(); } } break; } } }