/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE file at the root of the source * tree and available online at * * https://github.com/keeps/roda */ package org.roda.wui.filter; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.roda.core.data.exceptions.AuthenticationDeniedException; import org.roda.core.data.exceptions.GenericException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * CAS client. * * Adapted from https://wiki.jasig.org/display/casum/restful+api. * * @author Rui Castro <rui.castro@gmail.com> */ public class CasClient { /** Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(CasClient.class); /** No ticket message. */ private static final String NO_TICKET = "Successful ticket granting request, but no ticket found!"; /** CAS server URL. */ private final String casServerUrlPrefix; /** * Constructor. * * @param casServerUrlPrefix * CAS server URL. */ public CasClient(final String casServerUrlPrefix) { this.casServerUrlPrefix = casServerUrlPrefix; } /** * Get a <strong>Ticket Granting Ticket</strong> from the CAS server for the * specified <i>username</i> and <i>password</i>. * * @param username * the username. * @param password * the password. * @return the <strong>Ticket Granting Ticket</strong> * @throws AuthenticationDeniedException * if the CAS server rejected the specified credentials. * @throws GenericException * if some error occurred. */ public String getTicketGrantingTicket(final String username, final String password) throws AuthenticationDeniedException, GenericException { final HttpClient client = new HttpClient(); final PostMethod post = new PostMethod(String.format("%s/v1/tickets", this.casServerUrlPrefix)); post.setRequestBody( new NameValuePair[] {new NameValuePair("username", username), new NameValuePair("password", password)}); try { client.executeMethod(post); final String response = post.getResponseBodyAsString(); if (post.getStatusCode() == HttpStatus.SC_CREATED) { final Matcher matcher = Pattern.compile(".*action=\".*/(.*?)\".*").matcher(response); if (matcher.matches()) { return matcher.group(1); } LOGGER.warn(NO_TICKET); throw new GenericException(NO_TICKET); } else if (post.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { throw new AuthenticationDeniedException("Could not create ticket: " + post.getStatusText()); } else { LOGGER.warn(invalidResponseMessage(post)); throw new GenericException(invalidResponseMessage(post)); } } catch (final IOException e) { throw new GenericException(e.getMessage(), e); } finally { post.releaseConnection(); } } /** * Get a <strong>Service Ticket</strong> from the CAS server for the specified * <strong>Ticket Granting Ticket</strong> and <strong>service</strong>. * * @param ticketGrantingTicket * the <strong>Ticket Granting Ticket</strong>. * @param service * the service URL. * @return the <strong>Service Ticket</strong>. * @throws GenericException * if some error occurred. */ public String getServiceTicket(final String ticketGrantingTicket, final String service) throws GenericException { final HttpClient client = new HttpClient(); final PostMethod post = new PostMethod(String.format("%s/v1/tickets/%s", casServerUrlPrefix, ticketGrantingTicket)); post.setRequestBody(new NameValuePair[] {new NameValuePair("service", service)}); try { client.executeMethod(post); final String response = post.getResponseBodyAsString(); if (post.getStatusCode() == HttpStatus.SC_OK) { return response; } else { LOGGER.warn(invalidResponseMessage(post)); throw new GenericException(invalidResponseMessage(post)); } } catch (final IOException e) { throw new GenericException(e.getMessage(), e); } finally { post.releaseConnection(); } } /** * Logout from the CAS server. * * @param ticketGrantingTicket * the <strong>Ticket Granting Ticket</strong>. * @throws GenericException * if some error occurred. */ public void logout(final String ticketGrantingTicket) throws GenericException { final HttpClient client = new HttpClient(); final DeleteMethod method = new DeleteMethod( String.format("%s/v1/tickets/%s", casServerUrlPrefix, ticketGrantingTicket)); try { client.executeMethod(method); if (method.getStatusCode() == HttpStatus.SC_OK) { LOGGER.info("Logged out"); } else { LOGGER.warn(invalidResponseMessage(method)); throw new GenericException(invalidResponseMessage(method)); } } catch (final IOException e) { throw new GenericException(e.getMessage(), e); } finally { method.releaseConnection(); } } /** * Returns an error message for invalid response from CAS server. * * @param method * the HTTP method * @return a String with the error message. */ private String invalidResponseMessage(final HttpMethod method) { return String.format("Invalid response from CAS server: %s - %s", method.getStatusCode(), method.getStatusText()); } }