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
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
94
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
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
165 public void destroy() {
166 }
167 }