001/* 002 * Copyright (c) 2000-2003 Yale University. All rights reserved. 003 * 004 * THIS SOFTWARE IS PROVIDED "AS IS," AND ANY EXPRESS OR IMPLIED 005 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 006 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE EXPRESSLY 007 * DISCLAIMED. IN NO EVENT SHALL YALE UNIVERSITY OR ITS EMPLOYEES BE 008 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 009 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED, THE COSTS OF 010 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR 011 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 012 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 013 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 014 * SOFTWARE, EVEN IF ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH 015 * DAMAGE. 016 * 017 * Redistribution and use of this software in source or binary forms, 018 * with or without modification, are permitted, provided that the 019 * following conditions are met: 020 * 021 * 1. Any redistribution must include the above copyright notice and 022 * disclaimer and this list of conditions in any related documentation 023 * and, if feasible, in the redistributed software. 024 * 025 * 2. Any redistribution must include the acknowledgment, "This product 026 * includes software developed by Yale University," in any related 027 * documentation and, if feasible, in the redistributed software. 028 * 029 * 3. The names "Yale" and "Yale University" must not be used to endorse 030 * or promote products derived from this software. 031 */ 032 033package edu.yale.its.tp.cas.client.taglib; 034 035import java.io.*; 036import java.util.*; 037import javax.servlet.*; 038import javax.servlet.http.*; 039import javax.servlet.jsp.*; 040import javax.servlet.jsp.tagext.*; 041import edu.yale.its.tp.cas.client.*; 042import javax.xml.parsers.ParserConfigurationException; 043import org.xml.sax.SAXException; 044 045/** 046 * <p> 047 * Authentication tag for use with the Yale Central Authentication Service. 048 * </p> 049 * <p> 050 * Typical usage involves placing the tag at the top of the page. The tag checks to determine if the attribute 051 * referenced by id/scope exists; if it does, the tag has no runtime effect. If the attribute does not exist, however, a 052 * CAS authentication is necessary: if no ticket is present, we redirect to CAS, and if a ticket is present, we validate 053 * it. Upon successful CAS authentication (either by a pre-existing attribute or through CAS directly), we store the 054 * NetID in the attribute referenced by id/scope. 055 * </p> 056 * 057 * @author Shawn Bayern 058 * @author Drew Mazurek 059 */ 060public class AuthTag extends TagSupport { 061 062 // ********************************************************************* 063 // Internal state 064 065 private String var; // tag attribute 066 067 private int scope; // tag attribute 068 069 private String casLogin, casValidate, service; // from children 070 071 private List acceptedProxies; // from children 072 073 private HttpServletRequest request; 074 075 private HttpServletResponse response; 076 077 // ********************************************************************* 078 // Tag logic 079 080 public int doStartTag() throws JspException { 081 // retrieve and save the request and response objects 082 request = (HttpServletRequest) pageContext.getRequest(); 083 response = (HttpServletResponse) pageContext.getResponse(); 084 085 // reset invocation-specific state 086 casLogin = null; 087 casValidate = null; 088 try { 089 service = Util.getService(request, 090 (String) pageContext.getServletContext().getInitParameter("edu.yale.its.tp.cas.serverName")); 091 } catch (ServletException ex) { 092 throw new JspException(ex); 093 } 094 acceptedProxies = new ArrayList(); 095 return EVAL_BODY_INCLUDE; 096 } 097 098 public int doEndTag() throws JspTagException { 099 try { 100 // if our attribute's already present, don't do anything 101 if (pageContext.getAttribute(var, scope) != null) 102 return EVAL_PAGE; 103 104 // otherwise, we need to authenticate via CAS 105 String ticket = request.getParameter("ticket"); 106 107 // no ticket? redirect... 108 if (ticket == null || ticket.equals("")) { 109 if (casLogin == null) 110 throw new JspTagException("for pages that expect to be called without 'ticket' parameter, " 111 + "cas:auth must have a cas:loginUrl subtag"); 112 response.sendRedirect(casLogin + "?service=" + service); 113 return SKIP_PAGE; 114 } 115 116 // Yay, ticket! Validate it. 117 String netid = getAuthenticatedNetid(ticket); 118 if (netid == null) 119 throw new JspTagException("Unexpected CAS authentication error"); 120 121 // Store the authenticate user in the id/scope attribute 122 pageContext.setAttribute(var, netid, scope); 123 124 return EVAL_PAGE; 125 126 } catch (IOException ex) { 127 throw new JspTagException(ex.getMessage()); 128 } catch (SAXException ex) { 129 throw new JspTagException(ex.getMessage()); 130 } catch (ParserConfigurationException ex) { 131 throw new JspTagException(ex.getMessage()); 132 } 133 } 134 135 // ********************************************************************* 136 // Attribute accessors 137 138 public void setVar(String var) { 139 this.var = var; 140 } 141 142 public void setScope(String scope) { 143 if (scope.equals("page")) 144 this.scope = PageContext.PAGE_SCOPE; 145 else if (scope.equals("request")) 146 this.scope = PageContext.REQUEST_SCOPE; 147 else if (scope.equals("session")) 148 this.scope = PageContext.SESSION_SCOPE; 149 else if (scope.equals("application")) 150 this.scope = PageContext.APPLICATION_SCOPE; 151 else 152 throw new IllegalArgumentException("invalid scope"); 153 } 154 155 // ********************************************************************* 156 // Accessors for child tags 157 158 public void setCasLogin(String url) { 159 casLogin = url; 160 } 161 162 public void setCasValidate(String url) { 163 casValidate = url; 164 } 165 166 public void addAuthorizedProxy(String proxyId) { 167 acceptedProxies.add(proxyId); 168 } 169 170 public void setService(String service) { 171 this.service = service; 172 } 173 174 // ********************************************************************* 175 // Constructor and lifecycle management 176 177 public AuthTag() { 178 super(); 179 init(); 180 } 181 182 // Releases any resources we may have (or inherit) 183 public void release() { 184 super.release(); 185 init(); 186 } 187 188 // clears any internal state we might have 189 private void init() { 190 var = null; 191 scope = PageContext.PAGE_SCOPE; 192 casLogin = null; 193 casValidate = null; 194 acceptedProxies = null; 195 } 196 197 // ********************************************************************* 198 // Utility methods 199 200 private String getAuthenticatedNetid(String ticket) throws ParserConfigurationException, SAXException, IOException, 201 JspTagException { 202 ProxyTicketValidator pv = new ProxyTicketValidator(); 203 pv.setCasValidateUrl(casValidate); 204 pv.setServiceTicket(ticket); 205 pv.setService(service); 206 pv.validate(); 207 if (!pv.isAuthenticationSuccesful()) 208 throw new JspTagException("CAS authentication error: " + pv.getErrorCode()); 209 if (pv.getProxyList().size() != 0) { 210 // ticket was proxied 211 if (acceptedProxies.size() == 0) 212 throw new JspTagException("this page does not accept proxied tickets"); 213 else if (!acceptedProxies.contains(pv.getProxyList().get(0))) 214 throw new JspTagException("unauthorized top-level proxy: '" + pv.getProxyList().get(0) + "'"); 215 } 216 return pv.getUser(); 217 } 218}