View Javadoc

1   package com.sri.emo.auth;
2   
3   import java.io.IOException;
4   import java.security.cert.X509Certificate;
5   import java.util.HashMap;
6   import java.util.Map;
7   import java.util.StringTokenizer;
8   
9   import javax.servlet.Filter;
10  import javax.servlet.FilterChain;
11  import javax.servlet.FilterConfig;
12  import javax.servlet.ServletException;
13  import javax.servlet.ServletRequest;
14  import javax.servlet.ServletResponse;
15  import javax.servlet.http.HttpServlet;
16  import javax.servlet.http.HttpServletRequest;
17  
18  import org.apache.log4j.Logger;
19  
20  import com.jcorporate.expresso.core.db.DBException;
21  import com.jcorporate.expresso.core.logging.LogException;
22  import com.jcorporate.expresso.core.security.User;
23  import com.jcorporate.expresso.core.servlet.CheckLogin;
24  
25  
26  /***
27   * Allows for authentication against an X.509 certificate.
28   * @author Michael Rimov
29   */
30  public class SSLCertFilter extends HttpServlet implements Filter {
31  	/***
32  	 * Serial UUID.
33  	 */
34  	private static final long serialVersionUID = -7831564280696956812L;
35  
36  		private static final String X509_NAME = "javax.servlet.request.X509Certificate";
37  
38  	private static final String EMAIL_ADDRESS = "emailaddress";
39  
40  	private Logger log = Logger.getLogger(SSLCertFilter.class);
41  
42  	/***
43  	 * Currently does nothing.
44  	 */
45  	@SuppressWarnings("unused")
46  	public void init(FilterConfig filterConfig) throws ServletException {
47  
48  	}
49  
50  	// Process the request/response pair
51  	public void doFilter(ServletRequest request, ServletResponse response,
52  			FilterChain filterChain) throws IOException, ServletException{
53  			if (request.getAttribute(X509_NAME) != null) {
54  				if (log.isDebugEnabled()) {
55  					log.debug("Found X509 Certificate in request. "
56  							+ request.getAttribute(X509_NAME));
57  				}
58  
59  				X509Certificate[] cert = (X509Certificate[]) request
60  						.getAttribute(X509_NAME);
61  
62  				if (cert.length == 0) {
63  					throw new IllegalArgumentException(
64  							"Must supply X.509 Certificate if SSLCertFilter is installed");
65  				}
66  
67  				if (log.isDebugEnabled()) {
68  					log.debug("Certificate DN:"
69  							+ cert[0].getSubjectDN().getName());
70  					log.debug("Issuer DN:" + cert[0].getIssuerDN().getName());
71  				}
72  
73  				Map<String, String> compoundName = parseDN(cert[0]
74  						.getSubjectDN().getName());
75  				assert compoundName != null;
76  				if (compoundName.size() == 0) {
77  					log.error("Parsed Subject DN from the Certificate ( "
78  							+ cert[0].getSubjectDN().getName()
79  							+ ") didn't parse into any name value pairs.");
80  				}
81  
82  				if (!compoundName.containsKey(EMAIL_ADDRESS)) {
83  					log.error("There was no common name in the cert: "
84  							+ cert[0].getSubjectDN().getName());
85  				}
86  
87  				String mailAddress = compoundName.get(EMAIL_ADDRESS);
88  				assert mailAddress != null;
89  				if (mailAddress != null && mailAddress.length() > 0) {
90  					loginWithAddress(request,mailAddress);
91  				} else {
92  					log.error("There was no email address associated with the certificate " + cert[0].getSubjectDN().getName());
93                      // need to somehow get this info to user in browser...
94                      // throw here with good message?
95                  }
96  
97  			}
98  
99  			filterChain.doFilter(request, response);
100 	}
101 
102 	private void loginWithAddress(ServletRequest request, String mailAddress) throws ServletException {
103         try {
104         String dbToLogin = "default";
105 		User thisUser = new User();
106         thisUser.setDataContext(dbToLogin);
107 			thisUser.setEmail(mailAddress);
108 
109         if (!thisUser.find()) {
110         	throw new ServletException("Access Denied: Account for '" + mailAddress + "' does not exist." );
111         }
112 
113         if (!thisUser.getAccountStatus().equals("A")) {
114             throw new ServletException("Access denied: Expresso account '" + mailAddress
115                 + "' is not active.");
116         }
117 
118         if (log.isInfoEnabled()) {
119             log.info("User " + thisUser.getDisplayName() + " logged in via the container");
120         }
121 
122         CheckLogin.getInstance().doSuccessfulAuth((HttpServletRequest) request, thisUser.getUserInfo().getLoginName(), dbToLogin, thisUser);		
123 
124 		} catch (DBException e) {
125 			throw new ServletException("Database error authenticating " + mailAddress, e);
126 		} catch (LogException e) {
127 			throw new ServletException("Error performing authentication ", e);
128 		}
129 	}
130 
131 	/***
132 	 * Parses a comma delimited list of name value pairs.
133 	 * 
134 	 * @param name
135 	 *            a string containing the compound name to be parsed
136 	 * @return a HashMap object built from the parsed key-value pairs, with keys all lower-cased
137 	 * @throws IllegalArgumentException
138 	 *             if the compound name is invalid
139 	 * @throws NullPointerException
140 	 *             if name is null.
141 	 */
142 	private Map<String, String> parseDN(final String name) {
143 
144 		if (name == null) {
145 			throw new NullPointerException("name");
146 		}
147 		Map<String, String> returnMap = new HashMap<String, String>();
148 
149 		// Parse
150 		StringTokenizer st = new StringTokenizer(name, ",");
151 		while (st.hasMoreTokens()) {
152 			String pair = (String) st.nextToken();
153 			int pos = pair.indexOf('=');
154 			if (pos == -1) {
155 				throw new IllegalArgumentException("Malformed Name: " + name);
156 			}
157 			String key = pair.substring(0, pos).trim().toLowerCase();
158 			String val = pair.substring(pos + 1, pair.length()).trim();
159 			returnMap.put(key, val);
160 		}
161 		return returnMap;
162 	}
163 
164 	// Clean up resources
165 	public void destroy() {
166 	}
167 }