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    }