001/* 002 * (C) Copyright 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 static final long serialVersionUID = 1L; 066 067 private String var; // tag attribute 068 069 private int scope; // tag attribute 070 071 private String casLogin, casValidate, service; // from children 072 073 private List<String> acceptedProxies; // from children 074 075 private HttpServletRequest request; 076 077 private HttpServletResponse response; 078 079 // ********************************************************************* 080 // Tag logic 081 082 @Override 083 public int doStartTag() throws JspException { 084 // retrieve and save the request and response objects 085 request = (HttpServletRequest) pageContext.getRequest(); 086 response = (HttpServletResponse) pageContext.getResponse(); 087 088 // reset invocation-specific state 089 casLogin = null; 090 casValidate = null; 091 try { 092 service = Util.getService(request, 093 pageContext.getServletContext().getInitParameter("edu.yale.its.tp.cas.serverName")); 094 } catch (ServletException ex) { 095 throw new JspException(ex); 096 } 097 acceptedProxies = new ArrayList<>(); 098 return EVAL_BODY_INCLUDE; 099 } 100 101 @Override 102 public int doEndTag() throws JspTagException { 103 try { 104 // if our attribute's already present, don't do anything 105 if (pageContext.getAttribute(var, scope) != null) 106 return EVAL_PAGE; 107 108 // otherwise, we need to authenticate via CAS 109 String ticket = request.getParameter("ticket"); 110 111 // no ticket? redirect... 112 if (ticket == null || ticket.equals("")) { 113 if (casLogin == null) 114 throw new JspTagException("for pages that expect to be called without 'ticket' parameter, " 115 + "cas:auth must have a cas:loginUrl subtag"); 116 response.sendRedirect(casLogin + "?service=" + service); 117 return SKIP_PAGE; 118 } 119 120 // Yay, ticket! Validate it. 121 String netid = getAuthenticatedNetid(ticket); 122 if (netid == null) 123 throw new JspTagException("Unexpected CAS authentication error"); 124 125 // Store the authenticate user in the id/scope attribute 126 pageContext.setAttribute(var, netid, scope); 127 128 return EVAL_PAGE; 129 130 } catch (IOException ex) { 131 throw new JspTagException(ex.getMessage()); 132 } catch (SAXException ex) { 133 throw new JspTagException(ex.getMessage()); 134 } catch (ParserConfigurationException ex) { 135 throw new JspTagException(ex.getMessage()); 136 } 137 } 138 139 // ********************************************************************* 140 // Attribute accessors 141 142 public void setVar(String var) { 143 this.var = var; 144 } 145 146 public void setScope(String scope) { 147 if (scope.equals("page")) 148 this.scope = PageContext.PAGE_SCOPE; 149 else if (scope.equals("request")) 150 this.scope = PageContext.REQUEST_SCOPE; 151 else if (scope.equals("session")) 152 this.scope = PageContext.SESSION_SCOPE; 153 else if (scope.equals("application")) 154 this.scope = PageContext.APPLICATION_SCOPE; 155 else 156 throw new IllegalArgumentException("invalid scope"); 157 } 158 159 // ********************************************************************* 160 // Accessors for child tags 161 162 public void setCasLogin(String url) { 163 casLogin = url; 164 } 165 166 public void setCasValidate(String url) { 167 casValidate = url; 168 } 169 170 public void addAuthorizedProxy(String proxyId) { 171 acceptedProxies.add(proxyId); 172 } 173 174 public void setService(String service) { 175 this.service = service; 176 } 177 178 // ********************************************************************* 179 // Constructor and lifecycle management 180 181 public AuthTag() { 182 super(); 183 init(); 184 } 185 186 // Releases any resources we may have (or inherit) 187 @Override 188 public void release() { 189 super.release(); 190 init(); 191 } 192 193 // clears any internal state we might have 194 private void init() { 195 var = null; 196 scope = PageContext.PAGE_SCOPE; 197 casLogin = null; 198 casValidate = null; 199 acceptedProxies = null; 200 } 201 202 // ********************************************************************* 203 // Utility methods 204 205 private String getAuthenticatedNetid(String ticket) throws ParserConfigurationException, SAXException, IOException, 206 JspTagException { 207 ProxyTicketValidator pv = new ProxyTicketValidator(); 208 pv.setCasValidateUrl(casValidate); 209 pv.setServiceTicket(ticket); 210 pv.setService(service); 211 pv.validate(); 212 if (!pv.isAuthenticationSuccesful()) 213 throw new JspTagException("CAS authentication error: " + pv.getErrorCode()); 214 if (pv.getProxyList().size() != 0) { 215 // ticket was proxied 216 if (acceptedProxies.size() == 0) 217 throw new JspTagException("this page does not accept proxied tickets"); 218 else if (!acceptedProxies.contains(pv.getProxyList().get(0))) 219 throw new JspTagException("unauthorized top-level proxy: '" + pv.getProxyList().get(0) + "'"); 220 } 221 return pv.getUser(); 222 } 223}