/*
* (C) Copyright 2000-2003 Yale University. All rights reserved.
*
* THIS SOFTWARE IS PROVIDED "AS IS," AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE EXPRESSLY
* DISCLAIMED. IN NO EVENT SHALL YALE UNIVERSITY OR ITS EMPLOYEES BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED, THE COSTS OF
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* Redistribution and use of this software in source or binary forms,
* with or without modification, are permitted, provided that the
* following conditions are met:
*
* 1. Any redistribution must include the above copyright notice and
* disclaimer and this list of conditions in any related documentation
* and, if feasible, in the redistributed software.
*
* 2. Any redistribution must include the acknowledgment, "This product
* includes software developed by Yale University," in any related
* documentation and, if feasible, in the redistributed software.
*
* 3. The names "Yale" and "Yale University" must not be used to endorse
* or promote products derived from this software.
*/
package edu.yale.its.tp.cas.client;
import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.nuxeo.common.utils.URIUtils;
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;
import edu.yale.its.tp.cas.util.SecureURL;
/**
* Validates STs and optionally retrieves PGT IOUs. Designed with a bean-like interface for simplicity and generality.
*/
public class ServiceTicketValidator {
// *********************************************************************
// Private state
protected String casValidateUrl, proxyCallbackUrl, st, service, pgtIou, user, errorCode, errorMessage,
entireResponse;
protected boolean renew = false;
protected boolean attemptedAuthentication;
protected boolean successfulAuthentication;
// *********************************************************************
// Accessors
/**
* Sets the CAS validation URL to use when validating tickets and retrieving PGT IOUs.
*/
public void setCasValidateUrl(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(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(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.
*/
public void setServiceTicket(String x) {
this.st = x;
}
/**
* Sets the service to use when validating.
*/
public void setService(String x) {
this.service = x;
}
/**
* Returns the strongly authenticated username.
*/
public String getUser() {
return this.user;
}
/**
* Returns the PGT IOU returned by CAS.
*/
public String getPgtIou() {
return this.pgtIou;
}
/**
* Returns <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.
*/
public String getErrorMessage() {
return this.errorMessage;
}
/**
* Returns CAS's error code if authentication failed.
*/
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 {
if (casValidateUrl == null || st == null)
throw new IllegalStateException("must set validation URL and ticket");
clear();
attemptedAuthentication = true;
Map<String, String> urlParameters = new HashMap<String, String>();
urlParameters.put("service", service);
urlParameters.put("ticket", st);
if (proxyCallbackUrl != null) {
urlParameters.put("pgtUrl", proxyCallbackUrl);
}
if (renew) {
urlParameters.put("renew", "true");
}
String url = URIUtils.addParametersToURIQuery(casValidateUrl, urlParameters);
String response = SecureURL.retrieve(url, false);
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.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
r.setContentHandler(newHandler());
r.parse(new InputSource(new StringReader(response)));
}
}
// *********************************************************************
// Response parser
protected DefaultHandler newHandler() {
return new Handler();
}
protected class Handler extends DefaultHandler {
// **********************************************
// Constants
protected static final String AUTHENTICATION_SUCCESS = "cas:authenticationSuccess";
protected static final String AUTHENTICATION_FAILURE = "cas:authenticationFailure";
protected static final String PROXY_GRANTING_TICKET = "cas:proxyGrantingTicket";
protected static final String USER = "cas:user";
// **********************************************
// Parsing state
protected StringBuffer currentText = new StringBuffer();
protected boolean authenticationSuccess = false;
protected boolean authenticationFailure = false;
protected String netid, pgtIou, errorCode, errorMessage;
// **********************************************
// Parsing logic
public void startElement(String ns, String ln, String qn, 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(char[] ch, int start, int length) {
// store the body, in stages if necessary
currentText.append(ch, start, length);
}
public void endElement(String ns, String ln, 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) {
if (qn.equals(AUTHENTICATION_FAILURE))
errorMessage = currentText.toString().trim();
}
}
public void endDocument() throws SAXException {
// save values as appropriate
if (authenticationSuccess) {
ServiceTicketValidator.this.user = user;
ServiceTicketValidator.this.pgtIou = pgtIou;
ServiceTicketValidator.this.successfulAuthentication = true;
} else if (authenticationFailure) {
ServiceTicketValidator.this.errorMessage = errorMessage;
ServiceTicketValidator.this.errorCode = errorCode;
ServiceTicketValidator.this.successfulAuthentication = false;
} else
throw new SAXException("no indication of success of failure from CAS");
}
}
// *********************************************************************
// Utility methods
/**
* Clears internally manufactured state.
*/
protected void clear() {
user = pgtIou = errorMessage = null;
attemptedAuthentication = false;
successfulAuthentication = false;
}
}