1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.security.authentication;
15
16 import java.io.IOException;
17 import java.util.Collections;
18 import java.util.Enumeration;
19
20 import javax.servlet.RequestDispatcher;
21 import javax.servlet.ServletException;
22 import javax.servlet.ServletRequest;
23 import javax.servlet.ServletResponse;
24 import javax.servlet.http.HttpServletRequest;
25 import javax.servlet.http.HttpServletRequestWrapper;
26 import javax.servlet.http.HttpServletResponse;
27 import javax.servlet.http.HttpServletResponseWrapper;
28 import javax.servlet.http.HttpSession;
29
30 import org.eclipse.jetty.http.HttpHeaders;
31 import org.eclipse.jetty.http.HttpMethods;
32 import org.eclipse.jetty.http.MimeTypes;
33 import org.eclipse.jetty.security.ServerAuthException;
34 import org.eclipse.jetty.security.UserAuthentication;
35 import org.eclipse.jetty.server.AbstractHttpConnection;
36 import org.eclipse.jetty.server.Authentication;
37 import org.eclipse.jetty.server.Authentication.User;
38 import org.eclipse.jetty.server.Request;
39 import org.eclipse.jetty.server.UserIdentity;
40 import org.eclipse.jetty.util.MultiMap;
41 import org.eclipse.jetty.util.StringUtil;
42 import org.eclipse.jetty.util.URIUtil;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
45 import org.eclipse.jetty.util.security.Constraint;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public class FormAuthenticator extends LoginAuthenticator
63 {
64 private static final Logger LOG = Log.getLogger(FormAuthenticator.class);
65
66 public final static String __FORM_LOGIN_PAGE="org.eclipse.jetty.security.form_login_page";
67 public final static String __FORM_ERROR_PAGE="org.eclipse.jetty.security.form_error_page";
68 public final static String __FORM_DISPATCH="org.eclipse.jetty.security.dispatch";
69 public final static String __J_URI = "org.eclipse.jetty.security.form_URI";
70 public final static String __J_POST = "org.eclipse.jetty.security.form_POST";
71 public final static String __J_SECURITY_CHECK = "/j_security_check";
72 public final static String __J_USERNAME = "j_username";
73 public final static String __J_PASSWORD = "j_password";
74
75 private String _formErrorPage;
76 private String _formErrorPath;
77 private String _formLoginPage;
78 private String _formLoginPath;
79 private boolean _dispatch;
80
81 public FormAuthenticator()
82 {
83 }
84
85
86 public FormAuthenticator(String login,String error,boolean dispatch)
87 {
88 this();
89 if (login!=null)
90 setLoginPage(login);
91 if (error!=null)
92 setErrorPage(error);
93 _dispatch=dispatch;
94 }
95
96
97
98
99
100 @Override
101 public void setConfiguration(AuthConfiguration configuration)
102 {
103 super.setConfiguration(configuration);
104 String login=configuration.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE);
105 if (login!=null)
106 setLoginPage(login);
107 String error=configuration.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE);
108 if (error!=null)
109 setErrorPage(error);
110 String dispatch=configuration.getInitParameter(FormAuthenticator.__FORM_DISPATCH);
111 _dispatch = dispatch==null?_dispatch:Boolean.valueOf(dispatch);
112 }
113
114
115 public String getAuthMethod()
116 {
117 return Constraint.__FORM_AUTH;
118 }
119
120
121 private void setLoginPage(String path)
122 {
123 if (!path.startsWith("/"))
124 {
125 LOG.warn("form-login-page must start with /");
126 path = "/" + path;
127 }
128 _formLoginPage = path;
129 _formLoginPath = path;
130 if (_formLoginPath.indexOf('?') > 0)
131 _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
132 }
133
134
135 private void setErrorPage(String path)
136 {
137 if (path == null || path.trim().length() == 0)
138 {
139 _formErrorPath = null;
140 _formErrorPage = null;
141 }
142 else
143 {
144 if (!path.startsWith("/"))
145 {
146 LOG.warn("form-error-page must start with /");
147 path = "/" + path;
148 }
149 _formErrorPage = path;
150 _formErrorPath = path;
151
152 if (_formErrorPath.indexOf('?') > 0)
153 _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
154 }
155 }
156
157
158 public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
159 {
160 HttpServletRequest request = (HttpServletRequest)req;
161 HttpServletResponse response = (HttpServletResponse)res;
162 String uri = request.getRequestURI();
163 if (uri==null)
164 uri=URIUtil.SLASH;
165
166 mandatory|=isJSecurityCheck(uri);
167 if (!mandatory)
168 return _deferred;
169
170 if (isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())))
171 return Authentication.NOT_CHECKED;
172
173 HttpSession session = request.getSession(true);
174
175 try
176 {
177
178 if (isJSecurityCheck(uri))
179 {
180 final String username = request.getParameter(__J_USERNAME);
181 final String password = request.getParameter(__J_PASSWORD);
182
183 UserIdentity user = _loginService.login(username,password);
184 if (user!=null)
185 {
186 session=renewSessionOnAuthentication(request,response);
187
188
189 String nuri;
190 synchronized(session)
191 {
192 nuri = (String) session.getAttribute(__J_URI);
193 }
194
195 if (nuri == null || nuri.length() == 0)
196 {
197 nuri = request.getContextPath();
198 if (nuri.length() == 0)
199 nuri = URIUtil.SLASH;
200 }
201 response.setContentLength(0);
202 response.sendRedirect(response.encodeRedirectURL(nuri));
203
204 Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
205 session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
206 return new FormAuthentication(getAuthMethod(),user);
207 }
208
209
210 if (LOG.isDebugEnabled())
211 LOG.debug("Form authentication FAILED for " + StringUtil.printable(username));
212 if (_formErrorPage == null)
213 {
214 if (response != null)
215 response.sendError(HttpServletResponse.SC_FORBIDDEN);
216 }
217 else if (_dispatch)
218 {
219 RequestDispatcher dispatcher = request.getRequestDispatcher(_formErrorPage);
220 response.setHeader(HttpHeaders.CACHE_CONTROL,"No-cache");
221 response.setDateHeader(HttpHeaders.EXPIRES,1);
222 dispatcher.forward(new FormRequest(request), new FormResponse(response));
223 }
224 else
225 {
226 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
227 }
228
229 return Authentication.SEND_FAILURE;
230 }
231
232
233 Authentication authentication = (Authentication) session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
234 if (authentication != null)
235 {
236
237 if (authentication instanceof Authentication.User &&
238 _loginService!=null &&
239 !_loginService.validate(((Authentication.User)authentication).getUserIdentity()))
240 {
241
242 session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
243 }
244 else
245 {
246 String j_uri=(String)session.getAttribute(__J_URI);
247 if (j_uri!=null)
248 {
249 MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(__J_POST);
250 if (j_post!=null)
251 {
252 StringBuffer buf = request.getRequestURL();
253 if (request.getQueryString() != null)
254 buf.append("?").append(request.getQueryString());
255
256 if (j_uri.equals(buf.toString()))
257 {
258
259
260
261 session.removeAttribute(__J_POST);
262 Request base_request = (req instanceof Request)?(Request)req:AbstractHttpConnection.getCurrentConnection().getRequest();
263 base_request.setMethod(HttpMethods.POST);
264 base_request.setParameters(j_post);
265 }
266 }
267 else
268 session.removeAttribute(__J_URI);
269
270 }
271 return authentication;
272 }
273 }
274
275
276 if (_deferred.isDeferred(response))
277 return Authentication.UNAUTHENTICATED;
278
279
280 synchronized (session)
281 {
282
283 if (session.getAttribute(__J_URI)==null)
284 {
285 StringBuffer buf = request.getRequestURL();
286 if (request.getQueryString() != null)
287 buf.append("?").append(request.getQueryString());
288 session.setAttribute(__J_URI, buf.toString());
289
290 if (MimeTypes.FORM_ENCODED.equalsIgnoreCase(req.getContentType()) && HttpMethods.POST.equals(request.getMethod()))
291 {
292 Request base_request = (req instanceof Request)?(Request)req:AbstractHttpConnection.getCurrentConnection().getRequest();
293 base_request.extractParameters();
294 session.setAttribute(__J_POST, new MultiMap<String>(base_request.getParameters()));
295 }
296 }
297 }
298
299
300 if (_dispatch)
301 {
302 RequestDispatcher dispatcher = request.getRequestDispatcher(_formLoginPage);
303 response.setHeader(HttpHeaders.CACHE_CONTROL,"No-cache");
304 response.setDateHeader(HttpHeaders.EXPIRES,1);
305 dispatcher.forward(new FormRequest(request), new FormResponse(response));
306 }
307 else
308 {
309 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
310 }
311 return Authentication.SEND_CONTINUE;
312
313
314 }
315 catch (IOException e)
316 {
317 throw new ServerAuthException(e);
318 }
319 catch (ServletException e)
320 {
321 throw new ServerAuthException(e);
322 }
323 }
324
325
326 public boolean isJSecurityCheck(String uri)
327 {
328 int jsc = uri.indexOf(__J_SECURITY_CHECK);
329
330 if (jsc<0)
331 return false;
332 int e=jsc+__J_SECURITY_CHECK.length();
333 if (e==uri.length())
334 return true;
335 char c = uri.charAt(e);
336 return c==';'||c=='#'||c=='/'||c=='?';
337 }
338
339
340 public boolean isLoginOrErrorPage(String pathInContext)
341 {
342 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
343 }
344
345
346 public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
347 {
348 return true;
349 }
350
351
352
353 protected static class FormRequest extends HttpServletRequestWrapper
354 {
355 public FormRequest(HttpServletRequest request)
356 {
357 super(request);
358 }
359
360 @Override
361 public long getDateHeader(String name)
362 {
363 if (name.toLowerCase().startsWith("if-"))
364 return -1;
365 return super.getDateHeader(name);
366 }
367
368 @Override
369 public String getHeader(String name)
370 {
371 if (name.toLowerCase().startsWith("if-"))
372 return null;
373 return super.getHeader(name);
374 }
375
376 @Override
377 public Enumeration getHeaderNames()
378 {
379 return Collections.enumeration(Collections.list(super.getHeaderNames()));
380 }
381
382 @Override
383 public Enumeration getHeaders(String name)
384 {
385 if (name.toLowerCase().startsWith("if-"))
386 return Collections.enumeration(Collections.EMPTY_LIST);
387 return super.getHeaders(name);
388 }
389 }
390
391
392
393 protected static class FormResponse extends HttpServletResponseWrapper
394 {
395 public FormResponse(HttpServletResponse response)
396 {
397 super(response);
398 }
399
400 @Override
401 public void addDateHeader(String name, long date)
402 {
403 if (notIgnored(name))
404 super.addDateHeader(name,date);
405 }
406
407 @Override
408 public void addHeader(String name, String value)
409 {
410 if (notIgnored(name))
411 super.addHeader(name,value);
412 }
413
414 @Override
415 public void setDateHeader(String name, long date)
416 {
417 if (notIgnored(name))
418 super.setDateHeader(name,date);
419 }
420
421 @Override
422 public void setHeader(String name, String value)
423 {
424 if (notIgnored(name))
425 super.setHeader(name,value);
426 }
427
428 private boolean notIgnored(String name)
429 {
430 if (HttpHeaders.CACHE_CONTROL.equalsIgnoreCase(name) ||
431 HttpHeaders.PRAGMA.equalsIgnoreCase(name) ||
432 HttpHeaders.ETAG.equalsIgnoreCase(name) ||
433 HttpHeaders.EXPIRES.equalsIgnoreCase(name) ||
434 HttpHeaders.LAST_MODIFIED.equalsIgnoreCase(name) ||
435 HttpHeaders.AGE.equalsIgnoreCase(name))
436 return false;
437 return true;
438 }
439 }
440
441
442
443
444
445
446 public static class FormAuthentication extends UserAuthentication implements Authentication.ResponseSent
447 {
448 public FormAuthentication(String method, UserIdentity userIdentity)
449 {
450 super(method,userIdentity);
451 }
452
453 @Override
454 public String toString()
455 {
456 return "Form"+super.toString();
457 }
458 }
459 }