mirror of https://github.com/jacekkow/keycloak-protocol-cas

Jacek Kowalski
2023-04-26 b1c0c9d40edcf1877698afb865f46c7f498ce7d7
commit | author | age
57a6c1 1 package org.keycloak.protocol.cas.utils;
MP 2
3 import org.apache.http.HttpEntity;
4 import org.apache.http.HttpResponse;
281a7e 5 import org.apache.http.NameValuePair;
57a6c1 6 import org.apache.http.client.HttpClient;
281a7e 7 import org.apache.http.client.entity.UrlEncodedFormEntity;
57a6c1 8 import org.apache.http.client.methods.HttpPost;
281a7e 9 import org.apache.http.message.BasicNameValuePair;
57a6c1 10 import org.apache.http.entity.ContentType;
MP 11 import org.apache.http.entity.StringEntity;
12 import org.keycloak.connections.httpclient.HttpClientProvider;
13 import org.keycloak.models.KeycloakSession;
14
15 import javax.ws.rs.core.HttpHeaders;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.nio.charset.StandardCharsets;
1293d5 19 import java.text.SimpleDateFormat;
MP 20 import java.util.Date;
21 import java.util.UUID;
281a7e 22 import java.util.LinkedList;
MM 23 import java.util.List;
57a6c1 24
MP 25 public class LogoutHelper {
26     //although it looks alike, the CAS SLO protocol has nothing to do with SAML; so we build the format
27     //required by the spec manually
28     private static final String TEMPLATE = "<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"$ID\" Version=\"2.0\" IssueInstant=\"$ISSUE_INSTANT\">\n" +
29             "  <saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">@NOT_USED@</saml:NameID>\n" +
30             "  <samlp:SessionIndex>$SESSION_IDENTIFIER</samlp:SessionIndex>\n" +
31             "</samlp:LogoutRequest>";
32
281a7e 33     public static HttpEntity buildSingleLogoutRequest(String serviceTicket) throws IOException {
1293d5 34         String id = "ID_" + UUID.randomUUID().toString();
MP 35         String issueInstant = new SimpleDateFormat("yyyy-MM-dd'T'H:mm:ss").format(new Date());
36         String document = TEMPLATE.replace("$ID", id).replace("$ISSUE_INSTANT", issueInstant)
57a6c1 37                 .replace("$SESSION_IDENTIFIER", serviceTicket);
281a7e 38         List<NameValuePair> parameters = new LinkedList<>();
MM 39         parameters.add(new BasicNameValuePair("logoutRequest", document));
40         return new UrlEncodedFormEntity(parameters);
57a6c1 41     }
MP 42
43     public static void postWithRedirect(KeycloakSession session, String url, HttpEntity postBody) throws IOException {
44         HttpClient httpClient = session.getProvider(HttpClientProvider.class).getHttpClient();
45         for (int i = 0; i < 2; i++) { // follow redirects once
46             HttpPost post = new HttpPost(url);
47             post.setEntity(postBody);
48             HttpResponse response = httpClient.execute(post);
49             try {
50                 int status = response.getStatusLine().getStatusCode();
51                 if (status == 302 && !url.endsWith("/")) {
52                     String redirect = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
53                     String withSlash = url + "/";
54                     if (withSlash.equals(redirect)) {
55                         url = withSlash;
56                         continue;
57                     }
58                 }
59             } finally {
60                 HttpEntity entity = response.getEntity();
61                 if (entity != null) {
62                     InputStream is = entity.getContent();
63                     if (is != null)
64                         is.close();
65                 }
66
67             }
68             break;
69         }
70     }
71 }