/* vim: set ts=2 et sw=2 cindent fo=qroca: */ package com.globant.katari.cas; import java.io.IOException; import java.io.StringReader; import org.apache.commons.lang.Validate; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** Validates STs and optionally retrieves PGT IOUs. * * Does not force https. */ public class HttpServiceTicketValidator { /** The url to use to validate a ticket. */ private String casValidateUrl = null; private String proxyCallbackUrl; private String st = null; private String service; private String pgtIou; private String user; private String errorCode; private String errorMessage; private String entireResponse; private boolean renew = false; private boolean successfulAuthentication; /** Sets the CAS validation URL to use when validating tickets and retrieving * PGT IOUs. */ public void setCasValidateUrl(final String x) { this.casValidateUrl = x; } /** * Gets the CAS validation URL to use when validating tickets and * retrieving PGT IOUs. */ public String getCasValidateUrl() { return this.casValidateUrl; } /** * Sets the callback URL, owned logically by the calling service, to * receive the PGTid/PGTiou mapping. */ public void setProxyCallbackUrl(final String x) { this.proxyCallbackUrl = x; } /** Sets the "renew" flag on authentication. * * When set to "true", authentication will only succeed if this was an * initial login (forced by the "renew" flag being set on login). */ public void setRenew(final boolean b) { this.renew = b; } /** * Gets the callback URL, owned logically by the calling service, to * receive the PGTid/PGTiou mapping. */ public String getProxyCallbackUrl() { return this.proxyCallbackUrl; } /** Sets the ST to validate. * * @param x the service ticket. */ public void setServiceTicket(final String x) { this.st = x; } /** * Sets the service to use when validating. */ public void setService(final String x) { this.service = x; } /** * Returns the strongly authenticated username. */ public String getUser() { return this.user; } /** * Returns the PGT IOU returned by CAS. * * @return a string with the PGT IOU returned by CAS. */ public String getPgtIou() { return this.pgtIou; } /** Checks if the authentication was successful. * * @return <tt>true</tt> if the most recent authentication attempted * succeeded, <tt>false</tt> otherwise. */ public boolean isAuthenticationSuccesful() { return this.successfulAuthentication; } /** * Returns an error message if CAS authentication failed. * * @return a string with the error message. */ public String getErrorMessage() { return this.errorMessage; } /** * Returns CAS's error code if authentication failed. * * @return a string with the error code. */ public String getErrorCode() { return this.errorCode; } /** * Retrieves CAS's entire response, if authentication was succsesful. */ public String getResponse() { return this.entireResponse; } //********************************************************************* //Actuator public void validate() throws IOException, SAXException, ParserConfigurationException { Validate.notNull(casValidateUrl, "The cas validate url cannot be null"); Validate.notNull(st, "The ticket cannot be null"); clear(); StringBuffer sb = new StringBuffer(); sb.append(casValidateUrl); if (casValidateUrl.indexOf('?') == -1) { sb.append('?'); } else { sb.append('&'); } sb.append("service=" + service + "&ticket=" + st); if (proxyCallbackUrl != null) { sb.append("&pgtUrl=" + proxyCallbackUrl); } if (renew) { sb.append("&renew=true"); } String url = sb.toString(); String response = SecureUrl.retrieve(url); this.entireResponse = response; // parse the response and set appropriate properties if (response != null) { XMLReader r = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); r.setFeature("http://xml.org/sax/features/namespaces", false); r.setContentHandler(newHandler()); r.parse(new InputSource(new StringReader(response))); } } //********************************************************************* // Response parser protected DefaultHandler newHandler() { return new Handler(); } protected class Handler extends DefaultHandler { //********************************************** // Constants private static final String AUTHENTICATION_SUCCESS = "cas:authenticationSuccess"; private static final String AUTHENTICATION_FAILURE = "cas:authenticationFailure"; private static final String PROXY_GRANTING_TICKET = "cas:proxyGrantingTicket"; private static final String USER = "cas:user"; //********************************************** // Parsing state private StringBuffer currentText = new StringBuffer(); private boolean authenticationSuccess = false; private boolean authenticationFailure = false; private String pgtIou; private String errorCode; private String errorMessage; protected boolean getAuthenticationSuccess() { return authenticationSuccess; } protected String getCurrentText() { return currentText.toString().trim(); } //********************************************** // Parsing logic public void startElement(final String ns, final String ln, final String qn, final Attributes a) { // clear the buffer currentText = new StringBuffer(); // check outer elements if (qn.equals(AUTHENTICATION_SUCCESS)) { authenticationSuccess = true; } else if (qn.equals(AUTHENTICATION_FAILURE)) { authenticationFailure = true; errorCode = a.getValue("code"); if (errorCode != null) { errorCode = errorCode.trim(); } } } public void characters(final char[] ch, final int start, final int length) { // store the body, in stages if necessary currentText.append(ch, start, length); } public void endElement(final String ns, final String ln, final String qn) throws SAXException { if (authenticationSuccess) { if (qn.equals(USER)) { user = currentText.toString().trim(); } if (qn.equals(PROXY_GRANTING_TICKET)) { pgtIou = currentText.toString().trim(); } } else if (authenticationFailure && qn.equals(AUTHENTICATION_FAILURE)) { errorMessage = currentText.toString().trim(); } } public void endDocument() throws SAXException { // save values as appropriate if (authenticationSuccess) { HttpServiceTicketValidator.this.user = user; HttpServiceTicketValidator.this.pgtIou = pgtIou; HttpServiceTicketValidator.this.successfulAuthentication = true; } else if (authenticationFailure) { HttpServiceTicketValidator.this.errorMessage = errorMessage; HttpServiceTicketValidator.this.errorCode = errorCode; HttpServiceTicketValidator.this.successfulAuthentication = false; } else { throw new SAXException("no indication of success or failure from CAS"); } } } //********************************************************************* // Utility methods /** * Clears internally manufactured state. */ protected void clear() { user = null; pgtIou = null; errorMessage = null; successfulAuthentication = false; } /** Is this HttpServiceTicketValidator configured to pass renew=true on the * ticket validation request? @return true if renew=true on validation * reqeust, false otherwise. */ public boolean isRenew() { return renew; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("["); sb.append(HttpServiceTicketValidator.class.getName()); if (casValidateUrl != null) { sb.append(" casValidateUrl=["); sb.append(casValidateUrl); sb.append("]"); } if (proxyCallbackUrl != null) { sb.append(" proxyCallbackUrl=["); sb.append(proxyCallbackUrl); sb.append("]"); } if (st != null) { sb.append(" ticket=["); sb.append(st); sb.append("]"); } if (service != null) { sb.append(" service=["); sb.append(service); sb.append("]"); } if (pgtIou != null) { sb.append(" pgtIou=["); sb.append(pgtIou); sb.append("]"); } if (user != null) { sb.append(" user=["); sb.append(user); sb.append("]"); } if (errorCode != null) { sb.append(" errorCode=["); sb.append(errorCode); sb.append("]"); } if (errorMessage != null) { sb.append(" errorMessage=["); sb.append(errorMessage); sb.append("]"); } sb.append(" renew="); sb.append(renew); if (entireResponse != null) { sb.append(" entireResponse=["); sb.append(entireResponse); sb.append("]"); } sb.append("]"); return sb.toString(); } }