001 package edu.nrao.sss.webapp; 002 003 import java.io.IOException; 004 import java.io.StringReader; 005 import java.net.URL; 006 import java.net.URLEncoder; 007 008 import javax.servlet.Filter; 009 import javax.servlet.FilterChain; 010 import javax.servlet.FilterConfig; 011 import javax.servlet.ServletException; 012 import javax.servlet.ServletRequest; 013 import javax.servlet.ServletResponse; 014 import javax.servlet.http.HttpServletRequest; 015 import javax.servlet.http.HttpSession; 016 017 import org.apache.commons.httpclient.Header; 018 import org.apache.commons.httpclient.HttpClient; 019 import org.apache.commons.httpclient.HttpStatus; 020 import org.apache.commons.httpclient.NameValuePair; 021 import org.apache.commons.httpclient.methods.GetMethod; 022 import org.apache.commons.httpclient.methods.PostMethod; 023 import org.apache.log4j.Logger; 024 025 import edu.nrao.user.User; 026 027 /* 028 * This filter creates a User object from the XML it fetches from 029 * OpenSky's query service, the resulting object is stored in the user's 030 * session. This filter should be after the standard CAS filters in 031 * the filter chain as it looks up the XML by account name, which the 032 * other filters provide. 033 * 034 * It relies on several init parameters: 035 * 036 * <code>edu.nrao.sss.webapp.CASQueryURL</code>: the URL of the OpenSky 037 * query service (ex: https://mirror.nrao.edu:8443/nrao-2.0/secure/QueryFilter.htm). 038 * 039 * <code>edu.nrao.sss.webapp.CASQueryAccount</code>: the account to 040 * execute the query as (the query service itself requires authentication). 041 * 042 * <code>edu.nrao.sss.webapp.CASQueryPassword</code>: the password of 043 * the account to run the query as. 044 * 045 * <code>edu.nrao.sss.webapp.VerifyXML</code>: an optional parameter, 046 * whether or not to try to validate the XML opensky's service returns. This 047 * should be "true" or "false". If left empty or passed something else, will 048 * default to "true". 049 * 050 */ 051 public class LookupUserFilter implements Filter { 052 053 public final static String CASUser = "user"; 054 055 private static final Logger log = Logger.getLogger(LookupUserFilter.class); 056 057 private static final String INIT_PARAM_ACCOUNT = "edu.nrao.sss.webapp.CASQueryAccount"; 058 059 private static final String INIT_PARAM_PASSWORD = "edu.nrao.sss.webapp.CASQueryPassword"; 060 061 private static final String INIT_PARAM_URL = "edu.nrao.sss.webapp.CASQueryURL"; 062 063 private static final String INIT_PARAM_VERIFY_XML = "edu.nrao.sss.webapp.VerifyXML"; 064 065 private String casQueryAccount = null; 066 067 private String casQueryPassword = null; 068 069 private String casQueryURL = null; 070 071 private boolean verifyXML = true; 072 073 private static final String PROTOCOL = "https"; 074 075 private static final int PORT = 443; 076 077 public void destroy() { 078 } 079 080 public void doFilter(ServletRequest request, ServletResponse response, 081 FilterChain chain) throws IOException, ServletException { 082 HttpSession session = ((HttpServletRequest) request).getSession(); 083 String loginName = ((HttpServletRequest) request).getRemoteUser(); 084 085 if (session.getAttribute(CASUser) == null) { 086 User user = null; 087 log.info("CAS login by " + loginName); 088 try { 089 user = User.fromXml(new StringReader(getUserXML(loginName))); 090 } catch (Exception ex) { 091 throw new ServletException(ex); 092 } 093 session.setAttribute(CASUser, user); 094 } 095 chain.doFilter(request, response); 096 } 097 098 // Query OpenSky for the user's XML, this is cut and paste from Steve 099 // Riley's SecureQuery2 test code. Throws any number of rude exceptions. 100 // One weakness, at this point I don't think it handles failed logins 101 // correctly. 102 private String getUserXML(String accountName) throws Exception { 103 StringBuilder result = new StringBuilder(); 104 105 try { 106 URL u = new URL(casQueryURL + "?userByAccountNameEquals=" 107 + accountName); 108 HttpClient client = new HttpClient(); 109 client.getHostConfiguration().setHost(u.getHost(), PORT, PROTOCOL); 110 111 String path = u.getFile(); 112 GetMethod authget = new GetMethod(path); 113 client.executeMethod(authget); 114 115 int statuscode = authget.getStatusCode(); 116 if (statuscode != HttpStatus.SC_OK) { 117 authget.releaseConnection(); 118 log.error("query servlet returned response code "+statuscode); 119 throw new IOException("query servlet returned response code "+statuscode); 120 } 121 122 String response = authget.getResponseBodyAsString(); 123 // System.out.println("Server response:\n" + response.trim()); 124 125 int c1 = response.indexOf("name=\"lt\"", 0); 126 String lt = response.substring(c1 + 17, c1 + 93); 127 // System.out.println(lt); 128 String action = response.substring(response.indexOf("action=") + 8, 129 response.indexOf(" method=") - 1); 130 // System.out.println(action); 131 132 // release any connection resources used by the method 133 authget.releaseConnection(); 134 135 PostMethod authpost = new PostMethod(action); 136 // Prepare login parameters 137 NameValuePair ltP = new NameValuePair("lt", lt); 138 NameValuePair userid = new NameValuePair("username", URLEncoder 139 .encode(casQueryAccount, "UTF-8")); 140 NameValuePair passwd = new NameValuePair("password", URLEncoder 141 .encode(casQueryPassword, "UTF-8")); 142 NameValuePair evntid = new NameValuePair("_eventId", "submit"); 143 authpost.setRequestBody(new NameValuePair[] { userid, passwd, ltP, 144 evntid }); 145 146 authpost.addRequestHeader("Content-Type", 147 "application/x-www-form-urlencoded"); 148 authpost.addRequestHeader("Content-Length", String.valueOf(authpost 149 .getRequestEntity().getContentLength())); 150 151 client.executeMethod(authpost); 152 // System.out.println("Login form post: " + 153 // authpost.getStatusLine().toString()); 154 // System.out.println(authpost.getResponseBodyAsString()); 155 156 // release any connection resources used by the method 157 authpost.releaseConnection(); 158 159 statuscode = authpost.getStatusCode(); 160 if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) 161 || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) 162 || (statuscode == HttpStatus.SC_SEE_OTHER) 163 || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) { 164 Header header = authpost.getResponseHeader("location"); 165 if (header != null) { 166 String newuri = header.getValue(); 167 if ((newuri == null) || (newuri.equals(""))) { 168 newuri = "/"; 169 } 170 // System.out.println("Redirect target: " + newuri); 171 GetMethod redirect = new GetMethod(newuri); 172 173 client.executeMethod(redirect); 174 // System.out.println("Redirect: " + 175 // redirect.getStatusLine().toString()); 176 result.append(redirect.getResponseBodyAsString()); 177 // release any connection resources used by the method 178 redirect.releaseConnection(); 179 } else { 180 System.out.println("Invalid redirect"); 181 System.exit(1); 182 } 183 } 184 } catch (Exception exc) { 185 System.out.println(exc.toString()); 186 } 187 188 log.info("CAS Query returned:\n"+result.toString()); 189 return result.toString(); 190 } 191 192 // Pull in the init parameters. 193 public void init(FilterConfig config) throws ServletException { 194 casQueryAccount = config.getInitParameter(INIT_PARAM_ACCOUNT); 195 casQueryPassword = config.getInitParameter(INIT_PARAM_PASSWORD); 196 casQueryURL = config.getInitParameter(INIT_PARAM_URL); 197 String s = config.getInitParameter(INIT_PARAM_VERIFY_XML); 198 if (s != null && s.toLowerCase().equals("false")) 199 verifyXML = false; 200 } 201 }