/*
* (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.taglib;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import edu.yale.its.tp.cas.client.*;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
/**
* <p>
* Authentication tag for use with the Yale Central Authentication Service.
* </p>
* <p>
* Typical usage involves placing the tag at the top of the page. The tag checks to determine if the attribute
* referenced by id/scope exists; if it does, the tag has no runtime effect. If the attribute does not exist, however, a
* CAS authentication is necessary: if no ticket is present, we redirect to CAS, and if a ticket is present, we validate
* it. Upon successful CAS authentication (either by a pre-existing attribute or through CAS directly), we store the
* NetID in the attribute referenced by id/scope.
* </p>
*
* @author Shawn Bayern
* @author Drew Mazurek
*/
public class AuthTag extends TagSupport {
// *********************************************************************
// Internal state
private String var; // tag attribute
private int scope; // tag attribute
private String casLogin, casValidate, service; // from children
private List acceptedProxies; // from children
private HttpServletRequest request;
private HttpServletResponse response;
// *********************************************************************
// Tag logic
public int doStartTag() throws JspException {
// retrieve and save the request and response objects
request = (HttpServletRequest) pageContext.getRequest();
response = (HttpServletResponse) pageContext.getResponse();
// reset invocation-specific state
casLogin = null;
casValidate = null;
try {
service = Util.getService(request,
(String) pageContext.getServletContext().getInitParameter("edu.yale.its.tp.cas.serverName"));
} catch (ServletException ex) {
throw new JspException(ex);
}
acceptedProxies = new ArrayList();
return EVAL_BODY_INCLUDE;
}
public int doEndTag() throws JspTagException {
try {
// if our attribute's already present, don't do anything
if (pageContext.getAttribute(var, scope) != null)
return EVAL_PAGE;
// otherwise, we need to authenticate via CAS
String ticket = request.getParameter("ticket");
// no ticket? redirect...
if (ticket == null || ticket.equals("")) {
if (casLogin == null)
throw new JspTagException("for pages that expect to be called without 'ticket' parameter, "
+ "cas:auth must have a cas:loginUrl subtag");
response.sendRedirect(casLogin + "?service=" + service);
return SKIP_PAGE;
}
// Yay, ticket! Validate it.
String netid = getAuthenticatedNetid(ticket);
if (netid == null)
throw new JspTagException("Unexpected CAS authentication error");
// Store the authenticate user in the id/scope attribute
pageContext.setAttribute(var, netid, scope);
return EVAL_PAGE;
} catch (IOException ex) {
throw new JspTagException(ex.getMessage());
} catch (SAXException ex) {
throw new JspTagException(ex.getMessage());
} catch (ParserConfigurationException ex) {
throw new JspTagException(ex.getMessage());
}
}
// *********************************************************************
// Attribute accessors
public void setVar(String var) {
this.var = var;
}
public void setScope(String scope) {
if (scope.equals("page"))
this.scope = PageContext.PAGE_SCOPE;
else if (scope.equals("request"))
this.scope = PageContext.REQUEST_SCOPE;
else if (scope.equals("session"))
this.scope = PageContext.SESSION_SCOPE;
else if (scope.equals("application"))
this.scope = PageContext.APPLICATION_SCOPE;
else
throw new IllegalArgumentException("invalid scope");
}
// *********************************************************************
// Accessors for child tags
public void setCasLogin(String url) {
casLogin = url;
}
public void setCasValidate(String url) {
casValidate = url;
}
public void addAuthorizedProxy(String proxyId) {
acceptedProxies.add(proxyId);
}
public void setService(String service) {
this.service = service;
}
// *********************************************************************
// Constructor and lifecycle management
public AuthTag() {
super();
init();
}
// Releases any resources we may have (or inherit)
public void release() {
super.release();
init();
}
// clears any internal state we might have
private void init() {
var = null;
scope = PageContext.PAGE_SCOPE;
casLogin = null;
casValidate = null;
acceptedProxies = null;
}
// *********************************************************************
// Utility methods
private String getAuthenticatedNetid(String ticket) throws ParserConfigurationException, SAXException, IOException,
JspTagException {
ProxyTicketValidator pv = new ProxyTicketValidator();
pv.setCasValidateUrl(casValidate);
pv.setServiceTicket(ticket);
pv.setService(service);
pv.validate();
if (!pv.isAuthenticationSuccesful())
throw new JspTagException("CAS authentication error: " + pv.getErrorCode());
if (pv.getProxyList().size() != 0) {
// ticket was proxied
if (acceptedProxies.size() == 0)
throw new JspTagException("this page does not accept proxied tickets");
else if (!acceptedProxies.contains(pv.getProxyList().get(0)))
throw new JspTagException("unauthorized top-level proxy: '" + pv.getProxyList().get(0) + "'");
}
return pv.getUser();
}
}